Ceci est une ancienne révision du document !
Last month, we looked at playing some tricks on the ttk TNotebook widget with the style manipulation Python module that I wrote called “mystyles_dark.py”. I also promised that I would focus more on that module to provide some insight on how it all works.
Since then, I’ve modified the theme file so it isn’t quite as dark, and to do some other things that the original couldn’t. The new file is called “mystyles_notsodark.py”, and that’s what we’ll concentrate on in this month’s article. The files will be available in my repository, which I will share with you at the end of the article.
The original code was the starting point of my notsodark Tcl theme. I use the styling module as my testbed to make sure that I could make things work using Python, since my knowledge of Tcl/Tk was and still is limited. I looked at almost every Tcl theme I could, including the source for the four Linux “default” themes alt, clam, classic and of course default. I have to admit, it took me a while to figure out how to convert much of the Tcl code into Python. I had various copies of my beginning Tcl books open at the same time as I was trying to code the Python file and I had many false starts, but I eventually figured it all out. Eventually, I got the color schemes all worked out to something that I liked.
That’s enough history, now let’s get started.
The first thing we’ll look at is the styling module, “mystyles_notsodark.py”. The imports section is where I always start when examining a python module, so that’s where we’ll begin today.
import sys
import shared
import tkinter as tk
import os.path
Since we are making a “standalone” module, we need to import sys and tkinter. The use of os.path is needed since we are going to make the module operating system agnostic (able to run on Windows, Linux, and Mac), and we want to be able to get to the Assets folder no matter where the user has installed our module; we can use the os.path.join method to concatenate the correct directory separator (“/” or “\”) in the correct places. The other thing we need to import is an empty file called shared.py. We normally use this to enable various modules of a program to communicate between each other. However, there is a secondary benefit of this, which will become clear in a little bit.
We put the next three lines just after the import section so that the values are, by scope, implicity global. This means that they are available to any functions with the module as long as they aren’t changed within any of the functions. We have to be careful of this (see below).
We can use the location variable to provide the proper base path when we call the os.path.join statements later on.
Next, we need to do some definitions of color variables that we will need in our various functions. I’ll show only a few of the definitions, but you can look into the actual module. _bgcolor stands for background color, _fgcolor is the foreground color.
Now that we have all of our global definitions made, we can start with our first function (top right).
The create_styles function takes just one parameter, an instance of ttk.Style(). Since I like to save as many keystrokes as I can, I named that instance sty.
The first configure statement creates a set of color definitions that are duplicated from the global definitions. These, however, are supposed to be used for the root style of the theme, which sets the color sets for every ttk widget used in the Toplevel form. It doesn’t seem to actually work in Python like this and the actual colors that can be used are those from the global definition.
The next thing is to declare a map for the root style that controls how the widget colors will react to the various states like disabled, active, in focus, not in focus, and so on. Each item must be a list of tuples containing the state followed by the color, even if there is only one state that you are defining (bottom right).
Now we can start defining the styles for each widget (middle). I’ll show you only a few choice definitions to give you an idea how it’s done. Here’s the style for the Tbutton.
As before, there can be a section for the map of the state to color, and then a section for the “normal” look. If you want to create a style for all the widgets of a particular class, as in the above snippet, you must use the default style name. That almost always begins with a “T”.
When we want to set the attributes that we can’t normally set, we need to use the style.configure method. This allows us to set things like background and foreground colors, the padding (a list containing up to four integers for left, top, right and bottom in that order), font and so on.
The TButton also has a special style built in called Toolbutton. This, as well, can be overridden by using the same configure and map settings (top right).
As always, when using images, we have to keep a “temporary” copy of the image object, so Python’s garbage collection doesn’t happily delete it. Now you can see how we use the os.path.join function to define the location of the image we want and the need to use the shared.py file.
Next, we need to create a custom element that holds the information on which image is used for which state. This, of course, will change for different widgets and for various element parts. You can create images that will be used to control the shape of the widget and so on (next page, top right). Finally, we have to define the layout. This is the REALLY difficult part. Typically, a widget consists of one or more elements. For the TCheckbutton, there is a border, a padding, a focus and a label. This varies by widget class (next page, bottom right).
Finally, we can provide the map for the colors for each state (top left).
The styling module also provides a custom TRadiobutton definition.
I won’t go through all the various ttk widgets that the styling module handles. I tried to deal with every standard ttk widget available.
In the process of creating the actual Tcl theme, I added a function that looked important, but I didn’t really know what it did. It didn’t cause anything to throw any errors, so I just left it in. Eventually, I was trying to clean out some of the unneeded code and I came back to the function that I didn’t know what it did. It wasn’t in all the themes that I looked at in my learning process so I decided to look up what it was for. Here is the code (top right).
It turns out that the tk_setPalette function was created to allow for the normally non-themeable Tk widgets to get a pseudo theme applied by letting the tk_setPalette function override the normal database for the Tk widgets. We’ll see more about this when we get into the demo program code, which we’ll do now.
Below is what the PAGE designer version of the demo program looks like.
You can see that I’ve created the program to use two Labelframes, one for some “generic” Tk widgets and one for the ttk versions of them (where available). Now we’ll look at the code for the demo program.
In our demo program, we will skip all the normal imports and jump right into the startup function (bottom right).
You can see, we define our sty instance of the ttk.Style object, then we call the create_styles function of the styling module. Unfortunately, the ttk.TLabels don’t normally like to be changed via a style, so we use the function fix_Tlabels to set the proper foreground and background colors. Then I put some data into the Tk.Listbox and Tk.Textbox and finally start the TProgressbar which was set to Indeterminate mode in PAGE.
Here is the fix_TLabels function. I get the background color from the styling module, then create a list of all the TLabel aliases. Then I simply walk through the list, applying the background color (next page, top right).
If you look back at the image of the program in the PAGE designer, you will notice two checkboxes near the top of the form near the left side. One is labeled “Show the surprise” and the other “Disable Widgets”. We’ll deal with the “Disable Widgets” callback first (bottom right).
Again, I’m going to show only a portion of the widget list. Just like I did in the fix_Tlabels function, I created a list of the aliases of the widgets that I want to be able to control the disabled and normal states. This way, I can demonstrate the way the widgets respond in the different states. The actual list contains both the Tk and ttk widgets that respond to being disabled. I use variable che56, which PAGE assigned for me to find out if the checkbox is checked or not. Based on that, I walk through the widget list and set each widget to the proper disabled or normal state.
Below is the program running.
Now you can see the styling module in “action”. The TCheckbuttons and the TRadiobuttons are using the custom graphics, and the other ttk widgets now have the common style. I set the TCombobox, the TEntry widget, and the TSpinbox to have a complimentary background color that isn’t the standard bright white.
Now we can look at the “Show The Surprise” checkbutton callback. The idea here is to call the tk_setPalette function to show the ability to use the style on the Tk widgets (next page, top right).
To be able to set the palette back to the “normal” Tk colors, I had to duplicate the set_palette function and then I set the colors back to the standard colors.
Below is what it looks like when we have the “Show The Surprise” checkbutton and the “Disable Widgets” checkbutton selected at the same time.
All the widgets now are not only themed, but disabled, and you can see that it’s pretty obvious that the widgets are disabled. Even if the Radiobutton or Checkbuttons are selected, you can still tell those that are selected.
One additional feature that the set_Palette function provides is to set the color for the menubar as well.
All the files for this project are located in my repository at https://github.com/gregwa1953/FCM194 .
Until next time, as always; stay safe, healthy, positive and creative!