Working with ttk-themed widgets

We are almost done programming our drum machine. However, we would like to end this chapter by introducing you to ttk-themed widgets. 

Tkinter does not bind to the native platform widgets on many platforms, such as Microsoft Windows and X11.

The Tk toolkit (and Tkinter) originally appeared on X-Window systems; hence, it adopted the motif style that was the de facto standard for GUI development on X-Window systems.

When Tk was ported to other platforms, such as Windows and Mac OS, this motif style started appearing out of place with the look of these platforms.

Due to this, some even argue that Tkinter widgets are rather ugly and do not integrate well with such desktop environments.

Another criticism of Tkinter is based on the fact that Tkinter mixes logic and styling by allowing both to be changed as widget options.

Tkinter was also criticized for lacking any kind of theming support. Although we saw an example of centralized styling via the option database, the method required styling to be done at the widget level. It does not allow for selective styling of two Button widgets differently, for example. This made it difficult for developers to implement visual consistency for similar groups of widgets while differentiating them from other groups of widgets.

As a result of this, many GUI developers moved to Tkinter alternatives, such as wxPython, PySide, and PyQT.

With Tkinter 8.5, the makers of Tkinter have tried to address all these concerns by introducing the ttk module, which may be considered as an advance to the original Tkinter module.

Let's take a look at some of the features offered by the ttk-themed widgets module. 

One of the first things that ttk does is provide a set of built-in themes that allows Tk widgets to look like the native desktop environment in which the application is running.

Additionally, it introduces 6 new widgets—Combobox, Notebook, ProgressbarSeparator, Sizegrip, and Treeview to the list of widgets, in addition to supporting 11 core Tkinter widgets, which are Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton, PanedWindow, Radiobutton, Scale, and Scrollbar.

To use the ttk module, we first import it into the current namespace:

from tkinter import ttk

You can display the ttk widgets as follows (see code 3.10.py):

ttk.Button(root, text='ttk Button').grid(row=1, column=1)
ttk.Checkbutton(root, text='tkCheckButton').grid(row=2, column=1)

Code 3.10.py provides a comparison of displays between the normal Tkinter widgets and the counterpart ttk widgets, as shown in the following screenshot:

Notice that the preceding screenshot is taken on a Microsoft Windows platform as the differences are more marked on systems that do not explicitly use the X-Window system. Notice how Tkinter widgets (on the left) look out of place on Microsoft Windows as compared to ttk widgets (on the right), which is the native Microsoft Windows look and feel (see code 3.10.py).

You can even override the basic Tkinter widgets by importing ttk after Tkinter as follows:
from tkinter import *
from tkinter.ttk import *
This causes all widgets common to Tk and ttk to be replaced by ttk widgets.
This has the direct benefit of using the new widgets, which gives a better look and feel across platforms.
However, the disadvantage of this kind of import is that you cannot distinguish the module from which the widget classes are imported. This is important because the Tkinter and ttk widget classes are not completely interchangeable. In this case, an unambiguous solution is to import them, as shown in the following code:
import tkinter as tk
from tkinter import ttk

Although most of the configuration options for Tkinter and ttk widgets are common, ttk-themed widgets do not support styling options such as fg, bg, relief, and border. This is purposefully removed from ttk in an attempt to keep logic and styling in different controls.

Instead, all styling-related options are handled by the respective style names. In a standard ttk module, each widget has an associated style name. You can retrieve the default style name of a widget using the widget.winfo_class() method.

For instance, consider a ttk Button:

>>> my_button = ttk.Button()
>>> my_button.winfo_class()

This prints Tbutton, which is the default style name for ttk.Button. For a list of default ttk style names for different widgets, refer to http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-style-layer.html.

In addition to the default style, you can assign a custom style class to a widget or group of widgets. To set up a new style, you use the following:

 style = ttk.Style()

To configure the style options for a default style, you use the command: 

style.configure('style.Defaultstyle', **styling options)

To create a new style from the built-in styles, define a style name of the form newName.oldName. For instance, to create an Entry widget to hold a date, you can call it Date.Tentry.

To use the new style on a widget, you use the command:

ttk.Widget(root, style='style.Defaultstyle') 

Next, we will discuss ttk theming.

The Style is used to control the appearance for individual widgets. Themes, on the other hand, control the appearance of the entire GUI. More simply put, a theme is a collection of styles. Grouping styles into themes lets the user switch designs for the entire GUI all at once. Like styles, all themes are uniquely identified by their name.

The list of available themes can be obtained as follows:

 >> from tkinter.ttk import *
>>> style = Style()
>>> style.theme_names()
('winnative', 'clam', 'alt', 'default', 'classic', 'xpnative')
To obtain the name of the currently active theme:
>>> style.theme_use()
'default'

You can change to another theme from the style.theme_names() list; use the following:

style.theme_use('your_new_theme_name') 

To explore various styling and theming-related options of ttk, refer to the dummy example (see code 3.11.py):

 from tkinter import Tk
from tkinter import ttk
root = Tk()
style = ttk.Style()
# defining the global style - applied when no other style is defined
style.configure('.', font='Arial 14', foreground='brown',
background='yellow')
# this label inherits the global style as style option not specified for it
ttk.Label(root, text='I have no style of my own').pack()
# defining a new style named danger and configuring its style only for the
# button widget
style.configure('danger.TButton', font='Times 12', foreground='red', padding=1)
ttk.Button(root, text='Styled Dangerously', style='danger.TButton').pack()
# Different styling for different widget states
style.map("new_state_new_style.TButton", foreground=[('pressed', 'red'), ('active', 'blue')])
ttk.Button(text="Different Style for different states",style="new_state_new_style.TButton").pack()
# Overriding current theme styles for the Entry widget
current_theme = style.theme_use()
style.theme_settings( current_theme,
{"TEntry":
{"configure":
{"padding": 10},
"map": {"foreground": [("focus", "red")] }
}
})
print(style.theme_names())
print(style.theme_use())
# this is effected by change of themes even though no style specified
ttk.Entry().pack()
root.mainloop()

The description of the code is as follows:

  • The first three lines of code import Tkinter and ttk, and set up a new root window.
  • The next line, style = ttk.Style(), defines a new style.
  •  The next line configures a program-wide style configuration using style.configure. The dot character (.), which is the first argument of configure, means that this style would apply to the Toplevel window and to all its child elements. This is the reason why all of our widgets get to have a yellow background.
  • The next line creates an extension (danger) to the default style (TButton). This is how you create custom styles, which are variations on a base default style. 
  • The next line creates a ttk.Label widget. Since we have not specified any style for this widget, it inherits the global style specified for the Toplevel window.
  • The next line creates a ttk.button widget and specifies it to be styled using our custom style definition of danger.TButton. This is why the foreground color of this button turns red. Notice how it still inherits the background color, yellow, from the global Toplevel style that we defined earlier.
  • The next two lines of code demonstrate how ttk allows for styling different widget states. In this example, we styled different states for a ttk.Button widget to display in different colors. Go ahead and click on this second button to see how different styles apply to different states of a button. Here, we use map(style, query_options, **kw) to specify dynamic values of style for changes in the state of the widget.
  • The next line fetches the current applicable theme. It then overrides some of the options for the theme's Entry widget using style.theme_settings('themename', ***options).
  • The next line defines an Entry widget but does not specify any style to it. It, therefore, inherits its properties from the theme we configured earlier. If you now type anything in this Entry widget, you will notice that it gets a padding of 10 px and the foreground text color is red inside the Entry widget.

Now that we know how to make our widgets look more like native platform widgets, let's change the Play and Stop buttons for our drum machine to ttk.button. Let's also change the Loop Checkbutton from Tkinter Checkbutton to ttk Checkbutton and add a few separators in the Play Bar section.

The following screenshots show the Play Bar before and after making the changes:

We first import ttk into our namespace and append ttk to the Play and Stop buttons as follows (code 3.12.py):

from tkinter import ttk

We then simply modify the buttons and Checkbutton in the create_play_bar, replacing button with ttk.Button, and loopbutton with ttk.Checkbutton:

 button = ttk.Button()
loopbutton = ttk.Checkbutton(**options)

Note that these changes make the Buttons and the Checkbutton look more like the native widgets of your working platform.

Finally, let's add ttk.separators to our Play Bar (see code 3.12.py). The format for adding separators is as follows:

ttk.Separator(playbar_frame, orient='vertical').grid(row=start_row, column = 5, sticky="ns", padx=5)

Note that we cannot change the buttons in the right-button matrix from button to ttk.Button. This is because ttk buttons do not support specifying options like background color.

This concludes the last iteration of this project. In this iteration, we first saw how and why to use ttk-themed widgets to improve the look and feel of our programs. 

We then used ttk Buttons and ttk Checkbuttons in our drum program to improve its look. We also saw the reasons why certain Tkinter Buttons in our program could not be replaced by ttk Buttons.

That brings us to the end of this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.37.250