Outils pour utilisateurs

Outils du site


issue177:python

Ceci est une ancienne révision du document !


Happy New Year! Let’s all hope that 2022 will bring great things to us all.

As most of you know, I work very closely with Don Rozenberg who created PAGE which is a GUI designer for Python. On 24 December, 2021, Don released version 7 of PAGE. While the latest version looks almost exactly like the previous version when you just look at the interface. The majority of the changes are under the hood. Let’s take a quick look at the parts of the interface that changed…

The first thing you will probably notice is that there is a new option provided at the top of the toolbox.

The Toplevel widget gives you the ability to add a new toplevel form to an existing project. In previous versions of PAGE you had to create a multiform project by creating a project for each form, which meant that there were three files (one .tcl and two .py) for each form. Now, since all forms can be part of the same project, there are only three files. More about this in a moment.

The next change you should notice is in the Widget Tree form.

It is much more comprehensive and functional.

One of the other (out of many others) new things won’t be seen unless you open the preferences menu (shown bottom middle).

On the Fonts tab is a new entry box that allows you to add more font sizes to the redesigned Font dialog. While there is a way to pick a one-time font size for a special purpose, you can use the Add Custom Font Sizes feature to pick font sizes that suit your needs.

There are a wealth of changes to the User Interface that we won’t touch on right now since we are going to discuss the biggest thing that has changed in PAGE 7.0. I’ll save them for you to find and I might touch on some of them in future articles. In my mind, the biggest change is the code creation engine, which takes the native .tk information from the GUI designer and turns it into Python code. It has changed significantly. Don has pretty much rewritten it from scratch.

When you created a GUI with PAGE Version 6, any Tk variables were exposed to your program through a special function called set_Tk_var. This would allow you to easily create dynamic labels or access and/or change various attributes of widgets like the TCombobox through the textvariable attribute (next page, top right).

This function was created by PAGE in the support module. As you can see from the code snippet above, if you wanted to change the text of the label, you could simply use

LabelDisplay.set()

to change the value to whatever you needed to display.

In the support module, PAGE 6 had a startup function called init which was the very last thing that was run before the user would see the form (bottom left).

Every form had this same function. To reference a widget directly we had to use w.widgetname to access that widget. This didn’t apply to the Tk variables, just the widgets.

Since PAGE 7 supports the ability to have multiple forms in one code module, changes to the paradigm had to change, since the possibility of variable conflicts became a very real concern. Assume that you have two forms both having a Label widget that has a textvariable called “LabelDisplay”. If this were to happen, you couldn’t be sure which Label widget would have it’s text changed. In order to keep this from happening, Don came up with a very creative way to keep this from happening. Here is a simple two form program that shows how easily it is taken care of (shown bottom right).

Both of the Label widgets have the same TK variable name DynLabel which, while not very good programming practice, is easy to do without thinking and under PAGE version 6 and below was completely reasonable. PAGE has had a feature called “borrow” for a long time and in version 7 has been enhanced to allow you to “borrow” forms and widgets from an earlier project that had multiple forms easily. All widgets and their variables can be imported into the new project. In order to keep this from being a problem, PAGE separates each form into its own class within the GUI module. Shown right is a quick snippet of what the GUI contains in version 7. Since each form is now defined as a separate class, the variable names can easily be the same between forms. I’ll show you (top right) how they are accessed in a moment. For now let’s take a look at the support module changes. There is no longer an init function, it is now called main.

The V7 in the snippet above is the actual project name. Each form now has an alias that starts with _w and then a number to reference the form, so _w1 is the alias for the widgets on form1 and _w2 refers to those on form2.

To change the text for the label on the first form, we would simply use the following code.

_w1.DynLabel.set(“Some Text”)

And to change the label text on the second form we would code it like this.

_w2.DynLabel.set(“Other Text”)

In order to reference the form itself in version 6, we used root., which allowed us to do things like move the form on the screen or to terminate the program. In version 7, you can see in the above code, root has been aliased to _top1 for the first form and _top2 for the second. It’s important to remember to use this new alias, since just using root will again cause some very unexpected (and often unwanted) issues.

To end the program in version 6, you would use the PAGE generated function destroy_window(). In PAGE 7, you simply call root.destroy(). Calling root from your code can be problematic, since all forms have a root. However, calling root.destroy() to terminate the program is fine, since it will close all active windows.

When you run a multiform project created with version 7, you might be surprised to see that all the forms show right away. If you look at the code again, you can see why. All of the forms get created at startup and when the root.mainloop line gets run, it shows everything that has been defined. Sometimes, that is the desired action and that’s ok. However, if you have an application that you want one form to be the main form and the others to only show on demand, that isn’t acceptable. It’s really easy to address this though. In version 6, I created two small functions in the support module that I called show_me and hide_me. When my main form needed to show another form, I would call the hide_me function to minimize the main form and then I would call the show_me function to show the second form. When the use of the second form was done, I would call the hide_me function for the second form and then the show_me function for the main form. It was a logical set of steps for the purpose. Here is what they looked like for version 6.

def show_me():

  global root
  root.deiconify()

def hide_me():

  global root
  root.withdraw()

We can’t use these functions directly in version 7, since they both call the root object directly. However, it’s simple to modify them to work properly. Simply create a set for each form.

def show_me1():

  global _top1
  _top1.deiconify()

def hide_me1():

  global _top1
  _top1.withdraw()

def show_me2():

  global _w2
  _w2.deiconify()

def hide_me2():

  global _w2
  _w2.withdraw()

You could even make a single set of functions that handles any number of forms by passing the object as a parameter to the function that defines the form you want to work with.

def show_form(which):

  which.deiconify()

def hide_form(which):

  which.withdraw()

To use this, we need to create a startup function that runs just before the forms are shown and we need to call it just before the last line of the main function.

You can use the startup function (or whatever you would like to call it) to do things like startup databases or initialize various settings and libraries. Our startup function, in this case, needs to have this at a minimum.

def startup():

  hide_form(_top2)

This will minimize or hide the second form, leaving the first (main) form visible. In order to show the second form and hide the first, we need a button on the main form to trigger the functions. We’ll call the button btnShow2 and define its callback function as on_btnShow2. In it’s callback, we would code it like this (bottom right).

(The lines that are not bold are part of the new callback function skeleton that PAGE creates.) This hides the first form and shows the second. Then for the callback to restore the first form and hide the second form, we do the following (bottom left).

Super easy and clean. All you have to remember is which form alias you need to use to manipulate that particular form.

I have to admit, it took a little while before I became completely comfortable with the changes, but they aren't as drastic as it might seem.

There are a number of other changes in PAGE version 7 that really enhance the capabilities, but I won’t go through all of them right now. If you want to have a quick and (somewhat) easy way to create Tkinter GUIs for your Python programs, you really need to get the latest version of PAGE. You can download it from https://sourceforge.net/projects/page/

Due to all the changes that PAGE 7 brings to the table, I wrote a new tutorial for new users and a document that shows an easy set of migration tips for those who are familiar with previous versions of PAGE. Both are included in every distribution of PAGE in the docs folder.

Until next time, as always; stay safe, healthy, positive and creative!

issue177/python.1643462261.txt.gz · Dernière modification : 2022/01/29 14:17 de auntiee