Giter Club home page Giter Club logo

ttkbootstrap's Introduction

Downloads Downloads

ttkbootstrap

English | δΈ­ζ–‡

A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap.

πŸ‘€ Check out the documentation.

1.0+ is a complete rebuild of the library. If you are using version 0.5 you may run into issues trying to import themes with the themes.json as this has been removed from 1.0. You can now import and save themes directly using the ttkcreator.

Features

βœ”οΈ Built-in Themes
Over a dozen curated dark and light themes.

βœ”οΈ Pre-defined Styles:
Loads of beautiful pre-defined widget styles such as outline and round toggle buttons.

βœ”οΈ Simple keyword API:
Apply colors and types using simple keywords such as primary and striped instead of the legacy approach of primary.Striped.Horizontal.TProgressbar. If you've used Bootstrap for web development, you are already familiar with this approach using css classes.

βœ”οΈ Lots of new Widgets:
ttkbootstrap comes with several new beautifully designed widgets such as Meter, DateEntry, and Floodgauge. Additionally, dialogs are now themed and fully customizable.

βœ”οΈ Built-in Theme Creator:
Want to create your own theme? Easy! ttkboostrap includes a built-in theme creator that enables you to easily build, load, expore, and apply your own custom themes.

Installation

python -m pip install ttkbootstrap

Simple Usage

Instead of using long, complicated ttk style classes, you can use simple keywords with the "bootstyle" parameter.

import ttkbootstrap as ttk
from ttkbootstrap.constants import *

root = ttk.Window(themename="superhero")

b1 = ttk.Button(root, text="Submit", bootstyle="success")
b1.pack(side=LEFT, padx=5, pady=10)

b2 = ttk.Button(root, text="Submit", bootstyle="info-outline")
b2.pack(side=LEFT, padx=5, pady=10)

root.mainloop()

The new keyword API is very flexible. The following examples all produce the same result:

  • bootstyle="info-outline"
  • bootstyle="info outline"
  • bootstyle=("info", "outline")
  • bootstyle=(INFO, OUTLINE)

Links

ttkbootstrap's People

Contributors

745404527 avatar alejandroautalan avatar antrrax avatar bigoulours avatar bszollosinagy avatar daniils avatar heribert17 avatar intellbg avatar israel-dryer avatar jongbatax avatar newbiexvwu avatar raylemon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ttkbootstrap's Issues

light color-bleed on dark themes

the highlight-border is coming through in the corners on dark-theme in the last update. This is slightly visible on the light-themes as well on the treeview.

Add custom radiobuttons for nicer look and cross-platform consistency

This is a potential improvement to create a consistent look & feel across platforms. Currently, the native windows buttons are used on windows, and standard clam theme on linux & mac. Also, these custom buttons look like the Bootstrap styled buttons on bootswatch.com, so that's a plus.

I could create images for the hover and pressed effects, as I did for the scale indicator, but not sure if it's strictly necessary.

light theme example
image

dark theme example
image

Unable to adjust font size

I'm not sure if it's a bug or something on my part, but I can't get this code below to adjust the font and/or size on ttk.Label even when I reconfigure the 'primary.TLabel'.

code:
from tkinter import *
from tkinter import ttk
from ttkbootstrap import Style

root = Tk()
style = Style(theme='journal')
style.configure('primary.TLabel',
font=('Arial', 25, 'bold')
)

main_notebook = ttk.Notebook(root)
main_notebook.pack(pady=15,padx=15)

master = ttk.Frame(main_notebook)
master.configure(width=300)

cal_Label = ttk.Label(master, text="Choose the starting date.", style='primary.TLabel' )
cal_Label.grid(row = 0, column = 0,sticky=W)

main_notebook.add(master, text='Test')
root.mainloop()

Provide visual representation of focus on Button

As a user using keyboard navigation, I want a way to visually display which widget get a focus.

Using ttkbootstrap, Button doesn't have anything to visually represent the focus. Thus making it hard for the user to know which widget got focus. Would recommend using something visual. Either change colour or adding border, etc.

No module named importlib.resources

Hey, I'm new to ttkbootstrap and wanted to start working with it, until i get an error during import.:

Traceback (most recent call last):
File "<pyshell#0>", line 1, in
import ttkbootstrap
File "C:\Users\JAMES UCHE\AppData\Local\Programs\Python\Python36-32\lib\site-packages\ttkbootstrap_init
.py", line 29, in
import importlib.resources
ModuleNotFoundError: No module named 'importlib.resources'_

err

I've tried, reinstalling ttkbootstrap, importlib, i've tried uninstalling importlib and still get the same error, 'import importlib' still works, but import importlib.resources doesn't, I've also tried upgrading the importlib version with 'pip install --upgrade importlib', still get the same error.

really need help with this, thanks

missing emojii support on some Linux distros causing ttkcreator to crash

On several linux distributions, there are no pre-installed fonts with emojii support. This causes the ttkcreator program to crash because it uses a paintbrush emojii on the color selector button.

The way to fix this is to install a font that supports emojii... sudo apt-get install fonts-symbola. However, it's not worth the hassle at this point for such a minor detail. So, I'm removing the emojii from the button until there is a better solution.

image

Add custom checkbuttons

This is a potential improvement to create a consistent look & feel across platforms. Currently, the native windows buttons are used on windows, and standard clam theme on linux & mac. Also, these custom buttons look like the Bootstrap styled buttons on bootswatch.com, so that's a plus.

testing version on a light theme using rounded rectangles, and the symbola truetype font, which is embedded

image

testing on a dark theme

image

replace `brightness` and `saturation` functions with consolidated change function

consolidate the new saturation and the existing brightness functions into a single function that looks something like the following, which will enable be to simultaneously change hue, saturation, and value:

def update_hsv(color, h=1, s=1, v=1):
    """
    Modify the hue, saturation, and/or value of a given hex color value.

    :param str color: the hexadecimal color value that is the target of hsv changes
    :param float h: change in hue as a floating number between -1.0 and 1.0
    :param float s: change in saturation as a floating number between -1.0 and 1.0
    :param float v: change in value as a floating number between -1.0 and 1.0

    :returns: a new hexadecimal color value that results from the hsv arguments passed into the function
    :rtype: str
"""
    # business logic here
    pass

Add style for readonly widgets

Some widgets support the readonly state, which currently doesn't look any different from the default state. Taking ttk.Entry as an example, I'd consider either making it similar to the disabled state but with more contrast to keep the text readable, or give it the same style as a ttk.Label, with the border only appearing on hover and/or focus.

How to include theme when using py2exe?

I am building my application using py2exe, but my software is unusable as it crashed. The reason for that is that the themes.json file (i renamed it to that) is missing... How do I solve this?

background is visible from padding when using an opaque image that fills the button

If I use an image for the button, I can see the default padding around the image. This may not be desirable behavior.

The padding makes the default button size look good. The only way to remove this post hoc is to remove the padding in the button constructor when an image is used padding=0. From my experience, most of the time that images are used on buttons, they are icons, in which case the extra padding is not noticeable, and probably desirable. So, these default settings might be a reasonable compromise between having to always add padding to make the default button size look okay, and sometimes having to remove padding when the image completely fills the button (and in the case when I don't want to see the button background coming through).

image

The result if I remove the padding from default settings. The padding is no longer visible (only the border). The button doesn't really look that bad. So this is a potential option.

image

The result if I remove padding in the button constructor: padding=0.

image

@daniilS, any thoughts on this issue? Have you had any problems with this?

Tk button style messes up Matplotlib toolbar

When using Matplotlib with the tk backend, the default styles for the tk widgets mess up the toolbar:
image
It can be avoided without too much trouble by subclassing matplotlib.backends.backend_tkagg.NavigationToolbar2Tk and editing its _Button method, but it's still something to be aware of in case there's a better solution for it.

(Also, this wouldn't need to be an issue if Matplotlib caught up with the times and just used ttk...)

change separator to runtime generated image

All of the built-in theme separators have a black highlight background that is conspicuous on light colored themes. It's not noticeable on dark themes, however. Creating an image based element, generated at runtime would give a much cleaner look. Generate the image similar to how the scale indicator is being drawn.

Only load the theme and assets for the selected theme

Currently I'm loading all themes, including generated assets, when the application is started. The _load_themes method is called in the Style constructor. Instead, I would like to generate the theme on demand. I can still load the settings file, but I would like to perhaps wait to instantiate a StylerTTK object in the theme_use method for the specific theme requested, and only if it has not been created already. I'll need to test how something like this would work with an application like the demo, where a style is changed multiple times after the mainloop is started. However, this is not a normal use-case.

update standard tkinter widgets

I had to remove most of the global styles that I applied to the standard tkinter widgets because it was interfering with my ttk styles. The solution is to use the option_add method to target specific legacy widgets. I've created a helper function to make this simpler... StylerTk._set_option.

For example...from within the StyleTk class:

self._set_option('checkbutton.selectColor, self.theme.colors.primary)

You have to use the option database names... which tend to following the camelCase naming convention. But, they can be found in the TCL/TK Man pages. For example: https://www.tcl.tk/man/tcl8.7/TkCmd/checkbutton.htm

I've reviewed most of the legacy widget that directly affect the ttk widgets I'm using. However, I need to go back through these one at a time to make sure they look okay in case someone decides to use them. Not a high priority, however, as they are not the intended target for this package.

Add visual style reference for all ttk widgets

Think about adding a visual style guide for all ttk elements to replace the table in the how to style section. Could also add some simple examples and provide other tips and tricks for each widget.

ttk.Separator causing lag when resizing screen

@daniilS , I was testing the performance of resizing the screen and noticed significant lag in resizing compared to the regular built-in themes when resizing the demo program. The culprit is the `ttk.Separator``, which I discovered when commenting out each widget to isolate the issue.

The think the reason this is happening is because tkinter replicates the image to fill in the additional space. With a 1x1 pixel image used as the source, it has to do this ALOT. Simply expanding the image size to 10x1 significantly improved performance. I'm still playing around the optimal size of this image, but this is something to keep in mind for future development.

This issue should only affect widgets which can be expanded... so this could also affects the Progress bar, technically. Though, I did not notice any performance issue with the Progress bar because the trough is already an 8x8 square. So the impact is felt less. Regardless, I'll test different sizes.

Created rounded scrollbars that can be styled with the theme colors

A user has asked about adding rounded scrollbars that can be themed (primary, secondary, etc...) similar to the breeze theme in the ttkthemes library.

The image below compares the rounded and the standard version of the scrollbar in the lumen theme.

The scrollbar thumb is slightly longer in the rounded version because it has no arrows. However, the relative percentage of the scrollview represented by the thumb is exactly the same.

image

Below is a the code for a very rough prototype to demonstrate how this could be done.

from PIL import Image, ImageDraw, ImageTk
from tkinter import ttk
from ttkbootstrap import Style, Colors

style = Style()

# draw the scrollbar thumb
im = Image.new('RGBA', (100, 250))
draw = ImageDraw.Draw(im)
draw.rounded_rectangle((8, 8, 92, 242), radius=42, fill=style.colors.primary)
sb_thumb = ImageTk.PhotoImage(im.resize((10, 25), Image.CUBIC))

# draw the scrollbar trough
im = Image.new('RGBA', (100, 250))
draw = ImageDraw.Draw(im)
draw.rounded_rectangle((1, 1, 99, 249), radius=48, fill=Colors.update_hsv(style.colors.bg, vd=-0.05),
                       outline=style.colors.border, width=2)
sb_trough = ImageTk.PhotoImage(im.resize((10, 25), Image.CUBIC))

# create new elements
style.element_create('Rounded.Vertical.Scrollbar.thumb', 'image', sb_thumb, border=(1, 4, 1, 4))
style.element_create('Rounded.Vertical.Scrollbar.trough', 'image', sb_trough, border=(1, 4, 1, 4), padding=0)

# create new style layout
style.layout('Rounded.Vertical.Scrollbar', [
    ('Rounded.Vertical.Scrollbar.trough', {'children': [
        ('Rounded.Vertical.Scrollbar.thumb', {'expand': 1, 'sticky': 'nswe'})],
        'sticky': 'ns'})])

# test the new style
root = style.master
root.geometry('400x400')

# add the existing scrollbar
old = ttk.Scrollbar(root, orient='vertical')
old.set(0.1, 0.7)
old.pack(side='right', fill='y', pady=10, padx=10)

# add the new scrollbar
new = ttk.Scrollbar(root, orient='vertical', style='Rounded.Vertical.Scrollbar')
new.set(0.1, 0.7)
new.pack(side='right', fill='y', padx=10, pady=10)

root.mainloop()

One potential issue is that when this new scrollbar is placed in a dark color frame in a light theme, the trough will appear to be squared because the progressbar background color defaults to the theme background color. The rounded edges of the trough do not contrast sufficiently against the light theme background when placed in a dark frame. This doesn't look terrible. The solution would be to configure the background color of the progressbar to match the parent background color, and then the rounded trough corners would be visible. However, it's not a deal-breaker in my opinion for most cases. And, dark themes will already be configured for this issue.

image

Doc fix for Custom Widget Styles

In each of the Widget Styles "Create a Custom Style" sections e.g. https://ttkbootstrap.readthedocs.io/en/latest/widgets/label.html#create-a-custom-style if the Style.configure method is used the way it is written it will result in an error due to the mixing of keyword and non-keyword arguments. The addition of the "style" keyword in each of these code snippets would fix this.

Style.configure(style='TLabel', font=('Helvetica', 12))
instead of
Style.configure('TLabel', font=('Helvetica', 12))

error when running creator on linux

crashing when running the following command on linux:

python -m creator

produces the following output

X Error of failed request: BadLength (poly request too large or internal Xlib length error)

This appears to be related to missing font.. I'm using an emoji on the buttons... so check to see if this is indeed the issue.

HOWTO set border of cells in Treeview header?

Hi
I saw that these things are supported in init section:

Treeheading.cell: background, rownumber
Treeheading.border: bordercolor, lightcolor, darkcolor, relief, borderwidth
Treeheading.padding: padding, relief, shiftrelief
Treeheading.image: image, stipple, background
Treeheading.text: text, font, foreground, underline, width, anchor, justify, wraplength, embossed

but I don't understand how to configure it.

class Tree(ttk.Treeview):
    def __init__(self, parent, **kwargs):
        super().__init__(parent, **kwargs)
        self.style = ttkbootstrap.Style()
        self.style.theme_use('lumen')
      
          # my  ugly try 
        self.style.configure("Treeheading.padding", padding=6, relief="flat", shiftrelief='flat') 
        self.style.configure('Treeheading', bordercolor='red', lightcolor='yellow', darkcolor='blue', relief='flat', borderwidth='3') 

then

        table_users.configure(columns=(0, 1, 2, 3, 4, 5), show="tree headings", selectmode='browse', height=30)
        table_users.heading('#0', text='Name')
        table_users.heading('#2', text='service')
        table_users.heading('#3', text='col3')
        table_users.heading('#4', text='col4')
        table_users.heading('#5', text='notes')

So the heading section of Treeview looks like solid. But I need split cells
Name | service | col3 | col4| notes
by white stripe to see and resize the columns

Provide a Entry Style to identity input that are invalid

As a developper, when adding danger.TEntry to a widget, I'm expecting the entry to have a particular style with red border similar to bootstrap <input class="has-error">.

In Bootstrap, it's possible to set a style to the the <input> to make the border red in way to tell the user there is a problem with this fields. I would expect to find something similar in ttkbootstrap. Initially, I was expecting this to be danger.TEntry
image

Remove duplicate theme 'Lumen'

Uses essentially the same color scheme as Yeti on the original website, but with a different relief... doesn't translate to this project so can be safely removed.

New Widget API in progress!

overview

I'm in the process of adding a new api for ttkbootstrap widgets, which will be located in the ttkbootstrap.widgets module. This requires quite a bit of refactoring and code optimization. However, it's going to be worth it. This will allow the user to customize the look of the widget as well as provide an easy-to-use keyword based style api. Additional functionality will be added on select widgets, and existing ttk widgets will still be able to use the style based api with legacy ttk.

The new ttkbootstrap widgets are going to be the best of both worlds... the flexibility of tkinter and the beauty of ttk.

The goal of this project is to allow the end-user to get a beautiful looking UI with as little code as possible. So, I'm not going to try to make everything completely customizable to the lowest grain of detail. You will be able to tweak the colors and other select attributes without having to build or interact with the ttk style api.. this by itself is a great boon for many tkinter users.... ttk widgets without having to learn the ttk style api..... You can, of course, still build your own styles if you wish.

Consider the following demo:

style = Style('superhero')
root = style.master
root.configure(background=style.colors.bg)
pack_settings = {'padx': 2, 'pady': 2, 'fill': 'x'}

# smart keyword based style builder
Button(root, text='default').pack(**pack_settings)
Button(root, text='secondary', bootstyle='secondary').pack(**pack_settings)
Button(root, text='info outline', bootstyle='info-outline').pack(**pack_settings)
Button(root, text='danger link', bootstyle='danger-link').pack(**pack_settings)

# customizable colors
Button(root, text='custom solid', background='purple', foreground='pink').pack(**pack_settings)
Button(root, text='custom outline', foreground='yellow', bootstyle='outline').pack(**pack_settings)
Button(root, text='custom link', foreground='orange', bootstyle='link').pack(**pack_settings)

root.mainloop()

This produces the following result:
image

keywords

The bootstyle keywords will be familiar to many in the webdev world who use the bootstrap css classes. This is the same idea. The bootstyle parameter builds the style option for ttk in the background by parsing out keywords that are tied to specific styles. The order and capitalization of the words DOES NOT MATTER. So, info-outline will work just as well as well as OUTLINE info. Though, for the sake of everyone, please stick to a pattern and be consistent. I will show a few variations in this document to illustrate what can be done, but the key generally is to be consistent with a convention that you choose.

This will be backwards compatible with existing ttk widgets. So, you can still use style="info.Outline.TButton" and the style will be rendered the same as if you used bootstyle="info outline"

other features

The options are going to be fully documented in the docstring and will be clearly visible in the widget constructor, which will be a huge improvement over legacy ttk classes. You can see this in the work-in-progress Button class below.

I'm going to add features to various widgets, such as automatic creation of variables and property getters and setters where appropriate. Creating new classes will also allow me to improve or modify existing widgets, such as allowing the scale output to be formatted or modified instead of defaulting to a float or integer.

library changes

Internally, the library will change quite a bit in how styles are implemented. I'm streamlining the style creation as well as making many of the class methods static to allow more flexibility. This is required in order to make it easy to build and use styles on-the-fly when customization are made in the widget constructor.

The code will be A LOT more concise when it's done. I'm refactoring a lot of the styles and improving the code base to be easier to read and modify.

I've added support for tkinter named colors so that I can use those with PIL when building image based layouts. This is not completely fleshed out yet for all platforms, but I hope to improve this in the future. If a named color is not found, I will simply default to a fallback color.

Below the ttkbootstrap.widgets.Button class as an example. This inherits from the ttkbootstrap.widgets.Widget class as well as the ttk.Button class.

class Button(Widget, ttk.Button):
    """A Button widget displays a textual string, bitmap or image. If text is displayed, it must all be in a single
    font, but it can occupy multiple lines on the screen (if it contains newlines or if wrapping occurs because of the
    wraplength option) and one of the characters may optionally be underlined using the underline option. It can
    display itself in either of three different ways, according to the state option; it can be made to appear raised,
    sunken, or flat; and it can be made to flash. When a user invokes the button (by pressing mouse button 1 with the
    cursor over the button), then the command specified in the command option is invoked.
    """

    def __init__(
        self,
        master=None,
        anchor=None,
        background=None,
        bootstyle="default",
        command=None,
        compound=None,
        cursor=None,
        font=None,
        foreground=None,
        image=None,
        state="normal",
        style=None,
        takefocus=True,
        textvariable=None,
        text=None,
        underline=None,
        **kw,
    ):
        """
        Args:
            master (Widget, optional): The parent widget.
            anchor (str, optional): Specifies how the information in the widget is positioned relative to the inner
                margins. Legal values are `n`, `ne`, `e`, `se`, `s`, `sw`, `w`, `nw`, and `center`.
            background (str, optional): The normal background color to use when displaying the widget. Setting this
                option will override all other style based background settings.
            bootstyle (str, optional): The **ttkbootstrap** style used to render the widget. This is a short-hand
                API for setting the widget style. You may also use the ``style`` option directly using the standard
                ``ttk`` API. Using the ``Style`` option will overwrite the ``bootstyle``.
            command (func, optional): A callback function to evaluate when the widget is invoked.
            compound (str, optional): Specifies if the widget should display text and bitmaps/images at the same time,
                and if so, where the bitmap/image should be placed relative to the text. Must be one of the values
                **none**, **bottom**, **top**, **left**, **right**, or **center**. For example, the (default) value
                **none** specifies that the bitmap or image should (if defined) be displayed `instead` of the text, the
                value **left** specifies that the bitmap or image should be displayed to the `left` of the text, and
                the value **center** specifies  that the bitmap or image should be displayed `underneath` the text.
            cursor (str, optional): Specifies the `mouse cursor`_ to be used for the widget. Names and values will
                vary according to your operating system.
            font (str, Font, optional): The font to use when drawing text inside the widget. The value may have any of
                the forms described in the `font manual page`_ under FONT DESCRIPTION.
            foreground (str, optional): The normal foreground color to use when displaying the widget. Setting this
                option will override all other style based foreground settings.
            image (PhotoImage, optional): Specifies an image to display in the widget, which must have been created
                with ``tk.PhotoImage`` or `TkPhotoImage`` if using **pillow**. Can also be a string representing the
                name of the photo if the photo has been given a name using the ``name`` parameter.  Typically, if
                the ``image`` option is specified then it overrides other options that specify a bitmap or textual
                value to display in the widget, though this is controlled by the ``compound`` option; the ``image``
                option may be reset to an empty string to re-enable a bitmap or text display.
            padding (str, optional): Specifies the internal padding for the widget. The padding is a list of up to four
                length specifications left top right bottom. If fewer than four elements are specified, bottom defaults
                to top, right defaults to left, and top defaults to left. In other words, a list of three numbers
                specify the left, vertical, and right padding; a list of two numbers specify the horizontal and the
                vertical padding; a single number specifies the same padding all the way around the widget.
            state (str, optional): May be set to ``normal`` or ``disabled`` to control the disabled state bit. This is
                a write-only option: setting it changes the widget state, but the state widget command does not affect
                the ``state`` option.
            style (str, optional): May be used to specify a custom widget style.
            takefocus (bool, optional): Determines whether the window accepts the focus during keyboard traversal
                (e.g., Tab and Shift-Tab). To remove the widget from focus traversal, use ``takefocus=False``.
            text (str, optional): Specifies a string to be displayed inside the widget.
            textvariable (Variable, optional): Specifies the name or instance of a tkinter variable whose value will be
                used in place of the ``text`` resource.
            underline (int, optional): Specifies the integer index of a character to underline in the widget. This
                option is used by the default bindings to implement keyboard traversal for menu buttons and menu
                entries. 0 corresponds to the first character of the text displayed in the widget, 1 to the next
                character, and so on.
            width (int, optional): If the label is text, this option specifies the absolute width of the text area on
                the button, as a number of characters; the actual width is that number multiplied by the average width
                of a character in the current font. For image labels, this option is ignored. The option may also be
                configured in a style.
            wraplength (int, optional): Specifies the maximum line length (in pixels). If this option is less than or
                equal to zero, then automatic wrapping is not performed; otherwise the text is split into lines such
                that no line is longer than the specified value.

        .. _`mouse cursor`: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/cursors.html
        """
        Widget.__init__(self, "TButton", master=master, bootstyle=bootstyle, style=style)

        self.tk = master.tk
        self.anchor = anchor
        self.background = background
        self.font = font
        self.foreground = foreground
        self.widget_id = None

        self.customized = False
        self.customize_widget()

        ttk.Button.__init__(
            self,
            master=master,
            command=command,
            compound=compound,
            cursor=cursor,
            image=image,
            state=state,
            style=self.style,
            takefocus=takefocus,
            text=text,
            textvariable=textvariable,
            underline=underline,
            **kw,
        )
        self.bind("<<ThemeChanged>>", self.on_theme_change)

    def customize_widget(self):

        if any([self.background != None, self.foreground != None, self.anchor != None, self.font != None]):
            self.customized = True

            if not self.widget_id:
                self.widget_id = uuid4() if self.widget_id == None else self.widget_id
                self.style = f"{self.widget_id}.{self.style}"

        if self.customized:
            options = {
                "theme": self.theme,
                "anchor": self.anchor,
                "background": self.background,
                "foreground": self.foreground,
                "font": self.font,
                "style": self.style,
            }

            if "Outline" in self.style:
                settings = StylerTTK.style_outline_buttons(**options)
            elif "Link" in self.style:
                settings = StylerTTK.style_link_buttons(**options)
            else:
                settings = StylerTTK.style_solid_buttons(**options)

            self.update_ttk_style(settings)

Symbola font increase .exe file size

First of all, I would like to congratulate and thank you for the work done with ttkbootstrap.

Unless I'm mistaken, the Symbola font is most responsible for the increase in file size when converting a script to exe (pyinstaller). Wouldn't it be possible to use another very small font, since very few characters from it are used?

In my case, when I added the ttkbootstrap library to the project, the file increased by approximately 2.6MB, 2MB being Symbola font.

Add a radial gauge to the widgets module

I'd like to include a widgets module with ttkbootstrap that includes custom widgets that are useful in modern UI's.

This radial gauge is a potential candidate (example is in vanilla ttk). I can create it with theme colors, and I've set it up to be adjustable in the constructor. Though, I'd probably want the customization to the style class instead of using the constructor to be consistent with the ttk api, at least as much as possible.

image

import tkinter as tk
from tkinter import ttk

from PIL import Image, ImageTk, ImageDraw


class Gauge(ttk.Label):

    def __init__(self, parent, **kwargs):
        self.arc = None
        self.im = Image.new('RGBA', (1000, 1000))
        self.min_value = kwargs.get('minvalue') or 0
        self.max_value = kwargs.get('maxvalue') or 100
        self.size = kwargs.get('size') or 200
        self.font = kwargs.get('font') or 'helvetica 12 bold'
        self.background = kwargs.get('background')
        self.foreground = kwargs.get('foreground') or '#777'
        self.troughcolor = kwargs.get('troughcolor') or '#e0e0e0'
        self.indicatorcolor = kwargs.get('indicatorcolor') or '#01bdae'
        self.arcvariable = tk.IntVar(value='text')
        self.arcvariable.trace_add('write', self.update_arcvariable)
        self.textvariable = tk.StringVar()
        self.setup()

        super().__init__(parent, image=self.arc, compound='center', style='Gauge.TLabel',
                         textvariable=self.textvariable, **kwargs)

    def setup(self):
        """Setup routine"""
        style = ttk.Style()
        style.configure('Gauge.TLabel', font=self.font, foreground=self.foreground)
        if self.background:
            style.configure('Gauge.TLabel', background=self.background)
        draw = ImageDraw.Draw(self.im)
        draw.arc((0, 0, 990, 990), 0, 360, self.troughcolor, 100)
        self.arc = ImageTk.PhotoImage(self.im.resize((self.size, self.size), Image.LANCZOS))

    def update_arcvariable(self, *args):
        """Redraw the arc image based on variable settings"""
        angle = int(float(self.arcvariable.get())) + 90
        self.im = Image.new('RGBA', (1000, 1000))
        draw = ImageDraw.Draw(self.im)
        draw.arc((0, 0, 990, 990), 0, 360, self.troughcolor, 100)
        draw.arc((0, 0, 990, 990), 90, angle, self.indicatorcolor, 100)
        self.arc = ImageTk.PhotoImage(self.im.resize((self.size, self.size), Image.LANCZOS))
        self.configure(image=self.arc)


if __name__ == '__main__':
    root = tk.Tk()
    style = ttk.Style()
    gauge = Gauge(root, padding=20)
    gauge.pack()
    ttk.Scale(root, from_=0, to=360, variable=gauge.arcvariable).pack(fill='x', padx=10, pady=10)

    # update the textvariable with the degrees information when the arcvariable changes
    gauge.arcvariable.trace_add('write', lambda *args, g=gauge: g.textvariable.set(f'{g.arcvariable.get()} deg'))

    root.mainloop()

DateEntry, TypeError: strptime() argument 1 must be str, not datetime.datetime

Using DateEntry and delete date from DateEntry then press Button to show calendar
but get this error:

ValueError: time data '202-08-0' does not match format '%Y-%m-%d'

this error, when delete all date from DateEntry
olddate = datetime.strptime(self.entry.get() or self.startdate, self.dateformat)
TypeError: strptime() argument 1 must be str, not datetime.datetime

i think the calendar should show even when no valid date in date entry

Add a rounded and squared togglebutton

@daniilS, the toggle style you added gave me the ability to create a rounded toggle switch. I'm modeling it after the one here. I'd like to have rounded and squared with images generated in the application at run-time so that I don't have to package the assets with the program. This also gives the ability to generate hover effects by dimming the color slightly with the update_hsv method.

The approach I took was to draw a rounded rectangle very large, and then shrink it to the size I need, apply anti-aliasing to get a nice smooth look. I'm not completely happy with the size or dimensions. But, it is getting close.

Let me know what you think if you have time to play around with it; and of course if you have any feedback.

rounded-toggle

from PIL import Image, ImageTk, ImageDraw
from tkinter import ttk


on_color='#2196F3'
off_color = '#cccccc'

# off image
off_im = Image.new('RGBA', (226, 130))
draw = ImageDraw.Draw(off_im)
draw.rounded_rectangle([1, 1, 225, 129], radius=(128/2), fill=off_color)
draw.ellipse([14, 14, 114, 114], fill='white')

# on image
on_im = Image.new('RGBA', (226, 130))
draw = ImageDraw.Draw(on_im)
draw.rounded_rectangle([1, 1, 225, 129], radius=(128/2), fill=on_color)
draw.ellipse([12, 12, 116, 116], fill='white')
on_im = on_im.transpose(Image.ROTATE_180)

# set up application styles
style = ttk.Style()
style.theme_use('clam')

# set the background white
style.configure('white.TFrame', background='white')
window = ttk.Frame(style.master, style='white.TFrame', padding=20)
window.pack(fill='both', expand='yes')

# save reference to images
toggle_off = ImageTk.PhotoImage(off_im.resize((32, 20), Image.LANCZOS))
toggle_on = ImageTk.PhotoImage(on_im.resize((32, 20), Image.LANCZOS))

# create a new image element
style.element_create('rounded.Toolbutton.label', 'image', toggle_on, ('!selected', toggle_off))

# set the new layout
style.layout('rounded.Toolbutton', [
    ('Toolbutton.border', {'sticky': 'nswe', 'children': [
        ('Toolbutton.padding', {'sticky': 'nswe', 'children': [
            ('rounded.Toolbutton.label', {'sticky': 'nswe'})]})]})])

# configure style settings
style.configure('rounded.Toolbutton', relief='flat', borderwidth=0)
style.map('rounded.Toolbutton',
          background=[
              ('selected', 'white'),
              ('!selected', 'white')])



# demo the new widget
ttk.Checkbutton(window, style='rounded.Toolbutton').pack()
window.mainloop()


Add a flood gauge style progressbar with label

The idea behind this widget style is that it is similar to a card you would see commonly on a dashboard that includes text. It is essentially a progressbar with text in the middle. And, it can be used exactly as a progressbar, with some adjustments to the text size and thickness.

This style can be created by adding a custom layout based on the progressbar and the label.

image

I've decided to use a brightened and desaturated color for the progressbar background so that I have no need to change the text color when the gauge is filled past the 50% mark.

The method below would be added to the StylerTTK class. The code currently reflects the primary color styles. The other colors would be the same logic, with just an iteration over the other available colors.

def _style_floodgauge(self):
    """
    Create a style configuration for the *ttk.Progressbar* that makes it into a floodgauge. Which is essentially
    a very large progress bar with text in the middle.

    The options available in this widget include:

        - Floodgauge.trough: borderwidth, troughcolor, troughrelief
        - Floodgauge.pbar: orient, thickness, barsize, pbarrelief, borderwidth, background
        - Floodgauge.text: 'text', 'font', 'foreground', 'underline', 'width', 'anchor', 'justify', 'wraplength',
            'embossed'
    """
    self.settings.update({
        'Floodgauge.trough': {'element create': ('from', 'clam')},
        'Floodgauge.pbar': {'element create': ('from', 'default')},
        'Horizontal.TFloodgauge': {
            'layout': [('Floodgauge.trough', {'children': [
                ('Floodgauge.pbar', {'side': 'left', 'sticky': 'ns'}),
                ("Floodgauge.label", {"sticky": ""})],
                'sticky': 'nswe'})],
            'configure': {
                'thickness': 100,
                'borderwidth': 1,
                'bordercolor': self.theme.colors.primary,
                'lightcolor': self.theme.colors.primary,
                'pbarrelief': 'flat',
                'troughcolor': Colors.update_hsv(self.theme.colors.primary, sd=-0.3, vd=0.8),
                'background': self.theme.colors.primary,
                'foreground': self.theme.colors.selectfg,
                'justify': 'center',
                'anchor': 'center',
                'font': 'helvetica 16'}},
        'Vertical.TFloodgauge': {
            'layout': [('Floodgauge.trough', {'children': [
                ('Floodgauge.pbar', {'side': 'bottom', 'sticky': 'we'}),
                ("Floodgauge.label", {"sticky": ""})],
                'sticky': 'nswe'})],
            'configure': {
                'thickness': 100,
                'borderwidth': 1,
                'bordercolor': self.theme.colors.primary,
                'lightcolor': self.theme.colors.primary,
                'pbarrelief': 'flat',
                'troughcolor': Colors.update_hsv(self.theme.colors.primary, sd=-0.3, vd=0.8),
                'background': self.theme.colors.primary,
                'foreground': self.theme.colors.selectfg,
                'justify': 'center',
                'anchor': 'center',
                'font': 'helvetica 16'}
        }})

Below is a prototype of the Floodgauge class. I will have to handle the label options separately because TCL doesn't understand a hybrid widget. The progressbar takes priority, so an error will occur if I try to pass through the text options to the superclass constructor. However, these options can still manipulated in the style since they actually exist in the layout. To get around this, I've created a hack that generates a unique style based on the one passed into the constructor that can be altered continuously. I believe this is similar to the approach taken by other developers (eg. PySimpleGUI) for creating custom styles on each button.

In the case of changing the text. I've set a trace on the textvariable so that it updates the widget every time the textvariable changes.

import tkinter as tk
from tkinter import ttk
from ttkbootstrap import Style
from uuid import uuid4


class Floodgauge(ttk.Progressbar):

    def __init__(self, parent, **kw):
        _style = kw.get('style') or 'TFloodgauge'
        _id = uuid4()
        _orient = kw.get('orient').title() or 'Horizontal'
        self._widgetstyle = f'{_id}.{_orient}.{_style}'
        parent.tk.call("ttk::style", "configure", self._widgetstyle, '-%s' % None, None, None)

        kwargs = {k: v for k, v in kw.items() if k not in ['text']}

        self.textvariable = kw.get('textvariable') or tk.StringVar(value=kw.get('text'))
        self.textvariable.trace_add('write', self._textvariable_write)
        self.variable = kw.get('variable') or tk.IntVar(value=kw.get('value') or 0)

        super().__init__(parent, class_='Floodgauge', style=self._widgetstyle, variable=self.variable, **kwargs)

    @property
    def text(self):
        return self.textvariable.get()

    @text.setter
    def text(self, value):
        self.textvariable.set(value)

    @property
    def value(self):
        return self.variable.get()

    @value.setter
    def value(self, value):
        self.variable.set(value)

    def _textvariable_write(self, *args):
        """
        Update the label text when there is a `write` action on the textvariable
        """
        self.tk.call("ttk::style", "configure", self._widgetstyle, '-%s' % 'text', self.textvariable.get(), None)


if __name__ == '__main__':
    root = tk.Tk()
    root.geometry('400x400')
    s = Style()
    p = Floodgauge(root, value=55, text='55', orient='vertical')


    def auto(progress):
        p.text = f'Memory Usage\n{p.value}%'
        p.step(1)
        p.after(50, auto, p)


    p.pack(fill='both', padx=20, pady=20, expand='yes')
    auto(p)
    root.mainloop()

Create a themed date chooser

@daniilS, I'm working on a themed date chooser which has 3-components....

  • a DateChooser class that creates the TopLevel window.
  • an ask_date function that opens the DateChooser and returns a datetime object.
  • a DateEntry class, that works like an entry field with a date chooser button attached.

I expect all of these to be used independently based on the needs of the situation.

date-picker

A few more things I still need to work out...

  • When using the DateEntry, the date chooser should use the date in the entry field as the default, so that if the button is invoked again, the calendar in the popup reflects the new date, not the current date as it does now.
  • I need to implement the themes for the various components so that they are changeable. Currently they are fixed to the primary theme color, and not inside of the class. Possibly I can subclass the ttk style and have something unique to the calendar widget, but I'll think about it.

Below is the proto-type code for this widget. It's not clean by any stretch, but it works.

import calendar
from datetime import datetime
from tkinter import IntVar, Toplevel, StringVar
from tkinter import ttk
from tkinter.ttk import Frame, Entry
from ttkbootstrap import Style


def ask_date(parent):
    outvar = StringVar()
    dp = DateChooserPopup(parent, outvar)
    if outvar.get():
        return datetime.strptime(outvar.get(), '%Y-%m-%d')


class DateEntry(Frame):

    def __init__(self, parent=None, dateformat='%Y-%m-%d', **kw):
        super().__init__(master=parent, **kw)
        self.parent = parent
        self.dateformat = dateformat
        self.entry = Entry(self)
        self.entry.pack(side='left', fill='x', expand='yes')
        self.button = ttk.Button(self, text='πŸ“…', command=self.on_date_select)
        self.button.pack(side='left')

        # insert default value
        self.entry.insert('end', datetime.today().strftime(dateformat))

    def on_date_select(self):
        date = ask_date(self.entry)
        self.entry.delete('0', 'end')
        self.entry.insert('end', date.strftime(self.dateformat))


class DateChooserPopup(Toplevel):

    def __init__(self, parent, variable=None, **kw):
        super().__init__()
        self.withdraw()
        self.transient(parent)
        self.parent = parent
        self.update_idletasks()  # actualize geometry
        x = parent.winfo_rootx() + parent.winfo_width()
        y = parent.winfo_rooty() + parent.winfo_height()
        self.overrideredirect(True)
        self.resizable(False, False)

        self.today = datetime.today()
        self.date = datetime.today()
        self.calendar = calendar.Calendar()
        self.cframe = ttk.Frame(self, padding=10, borderwidth=1, relief='raised')
        self.tframe = ttk.Frame(self.cframe)
        self.dframe = None
        self.titlevar = StringVar(value=f'{self.date.strftime("%B %Y")}')
        self.datevar = IntVar()
        self.variable = variable or StringVar()
        self.setup()
        self.geometry(f'+{x}+{y}')
        self.wait_window()

    def next_month(self):
        year, month = calendar._nextmonth(self.date.year, self.date.month)
        self.date = datetime(year=year, month=month, day=1)
        self.dframe.destroy()
        self.draw_calendar()

    def prev_month(self):
        year, month = calendar._prevmonth(self.date.year, self.date.month)
        self.date = datetime(year=year, month=month, day=1)
        self.dframe.destroy()
        self.draw_calendar()

    def setup(self):
        """Setup the calendar widget"""
        parent = self.parent
        self.cframe.pack(fill='both')
        self.tframe.pack(fill='x')

        self.calendar.setfirstweekday(calendar.SUNDAY)

        self.draw_titlebar()
        self.draw_calendar()

        self.deiconify()
        self.focus_set()

    def date_select(self, index):
        row, col = index
        date = self.monthdates[row][col].strftime('%Y-%m-%d')
        self.variable.set(date)
        self.after(10, self.destroy)

    def draw_titlebar(self):
        """Create the title bar"""
        # previous month button
        self.btn_prev = ttk.Button(self.tframe, text='Β«', style='primary.Link.TButton', command=self.prev_month)
        self.btn_prev.pack(side='left')
        # month and year title
        self.title_label = ttk.Label(self.tframe, textvariable=self.titlevar, anchor='center')
        self.title_label.pack(side='left', fill='x', expand='yes')
        # next month button
        self.btn_next = ttk.Button(self.tframe, text='Β»', style='primary.Link.TButton', command=self.next_month)
        self.btn_next.pack(side='left')

    def draw_calendar(self):
        self.titlevar.set(f'{self.date.strftime("%B %Y")}')
        self.monthdays = self.calendar.monthdayscalendar(self.date.year, self.date.month)
        self.monthdates = self.calendar.monthdatescalendar(self.date.year, self.date.month)

        self.dframe = ttk.Frame(self.cframe)
        self.dframe.pack(fill='both', expand='yes')

        # days of the week header
        for i, wd in enumerate(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']):
            wd_lbl = ttk.Label(self.dframe, text=wd, anchor='center', padding=(0, 5, 0, 10), width=4)
            wd_lbl.grid(row=0, column=i, sticky='nswe')

        # calendar days
        for row, wk in enumerate(self.monthdays):
            self.dframe.rowconfigure(row, weight=1)
            for col, day in enumerate(wk):
                self.dframe.columnconfigure(col, weight=1)
                if day == 0:
                    ttk.Label(self.dframe, text=self.monthdates[row][col].day, anchor='center',
                              style='secondary.TLabel',
                              padding=(0, 0, 0, 10)).grid(row=row + 1, column=col, sticky='nswe')
                else:
                    if all([
                        day == self.today.day,
                        self.date.month == self.today.month,
                        self.date.year == self.today.year]):

                        day_style = 'success.Toolbutton'
                    else:
                        day_style = 'calendar.primary.Outline.Toolbutton'

                    rb = ttk.Radiobutton(self.dframe, variable=self.datevar, value=day, text=day, style=day_style,
                                         padding=(0, 0, 0, 10), command=lambda x=row, y=col: self.date_select([x, y]))
                    rb.grid(row=row + 1, column=col, sticky='nswe')

    def define_style(self):
        pass


if __name__ == '__main__':
    # TODO setup the styling in the __init__ file, and setup the class so that it can be easilily modified.
    # TODO add documentation to all classes and methods.
    # TODO make sure the date chooser defaults to the entry field value instead of the current date.

    style = Style()
    style.configure('calendar.primary.Outline.Toolbutton', lightcolor='#fff', darkcolor='#fff', bordercolor='#fff')
    style.map('calendar.primary.Outline.Toolbutton',
              darkcolor=[
                  ('disabled', '#fff'),
                  ('pressed', '!disabled', '#273747'),
                  ('selected', '!disabled', '#273747'),
                  ('hover', '!disabled', '#2c3e50')],
              bordercolor=[
                  ('disabled', '#fff'),
                  ('pressed', '!disabled', '#273747'),
                  ('selected', '!disabled', '#273747'),
                  ('hover', '!disabled', '#2c3e50')],
              lightcolor=[
                  ('disabled', '#fff'),
                  ('pressed', '!disabled', '#273747'),
                  ('selected', '!disabled', '#273747'),
                  ('hover', '!disabled', '#2c3e50')])

    root = style.master
    picker = DateEntry(root, dateformat='%B %d, %Y', padding=20)
    picker.pack()
    root.mainloop()

Add striped progressbar

Bootstrap themes have a striped progressbar option; this can be added fairly easily with an image layout.

image

The image above was created with this simple style prototype which could be added as a new style class called striped.Horizontal.TProgressbar

from PIL import ImageDraw, Image, ImageTk
from tkinter import ttk
from ttkbootstrap import Colors

style = ttk.Style()
style.theme_use('clam')

# light theme colors
colors = Colors(**{
    "primary": "#4582ec",
    "secondary": "#adb5bd",
    "success": "#02b875",
    "info": "#17a2b8",
    "warning": "#f0ad4e",
    "danger": "#d9534f",
    "bg": "#ffffff",
    "fg": "#343a40",
    "selectbg": "#adb5bd",
    "selectfg": "#ffffff",
    "border": "#e5e5e5",
    "inputfg": "#343a40",
    "inputbg": "#f8f9fa"})

# progress bar colors
bar_primary = colors.primary
bar_secondary = Colors.update_hsv(bar_primary, vd=-0.05)

# progressbar image (create large and then resize to create smoother edges)
im = Image.new('RGBA', (100, 100), bar_secondary)
draw = ImageDraw.Draw(im)

# stripe 1
draw.polygon([(0, 0), (48, 0), (100, 52), (100, 100), (100, 100)], fill=bar_primary)

# stripe 2
draw.polygon([(0, 52), (48, 100), (0, 100)], fill=bar_primary)

# resize image and create PhotoImage instance for tkinter
indicator_img = ImageTk.PhotoImage(im.resize((25, 25), Image.LANCZOS))

# setup new style
style.element_create('Horizontal.Progressbar.pbar', 'image', indicator_img, width=25, sticky='ew')

style.layout('Horizontal.TProgressbar', [
    ('Horizontal.Progressbar.trough', {'sticky': 'nswe', 'children': [
        ('Horizontal.Progressbar.pbar', {'side': 'left', 'sticky': 'ns'})]})])

style.configure('Horizontal.TProgressbar', troughcolor=colors.inputbg)

# create progressbar and run program
ttk.Progressbar(orient='horizontal', value=75).pack(fill='x', padx=10, pady=10)
style.master.mainloop()

Combobox popdown color

The color for the Combobox*popdownlist is different than the background on the option menu background for dark themes. they should be the same.

labelframe border is being overwritten by tk default styles

the relief and borderwidth meant to set the defaults for the standard tkinter widget set is overriding the border width and relief for the ttk.Labelframe widget. Not sure why this would be the case, but I'll need to target specific widgets with default styles instead of using the master '.' widget for some features.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.