Outils pour utilisateurs

Outils du site


issue177:python

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.

Bonne année ! Espérons que 2022 nous apportera à tous de grandes choses.

Comme la plupart d'entre vous le savent, je travaille en étroite collaboration avec Don Rozenberg qui a créé PAGE, un concepteur d'interface graphique pour Python. Le 24 décembre 2021, Don a publié la version 7 de PAGE. Cependant, la dernière version ressemble presque exactement à la version précédente lorsque vous regardez tout simplement l'interface. La majorité des changements se trouve sous le capot. Jetons un coup d'œil rapide aux parties de l'interface qui ont changé…

La première chose que vous remarquerez probablement est qu'il existe une nouvelle option en haut de la boîte à outils.

Le widget Toplevel vous donne la possibilité d'ajouter un nouveau formulaire toplevel à un projet existant. Dans les versions précédentes de PAGE, vous deviez créer un projet multiforme en créant un projet pour chaque formulaire, ce qui signifiait qu'il y avait trois fichiers (un .tcl et deux .py) pour chaque formulaire. Maintenant, puisque tous les formulaires peuvent faire partie du même projet, il n'y a que trois fichiers. Nous reviendrons sur ce point dans un instant.

Le prochain changement que vous devriez remarquer concerne le formulaire de l'arbre des widgets.

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.

Il est beaucoup plus complet et fonctionnel.

L'une des nouveautés (parmi tant d'autres) ne sera visible que si vous ouvrez le menu des préférences (illustré en bas au milieu).

Dans l'onglet Polices, une nouvelle zone de saisie vous permet d'ajouter des tailles de police supplémentaires dans la boîte de dialogue Police, remaniée. Bien qu'il soit possible de choisir une taille de police unique pour un usage particulier, vous pouvez utiliser la fonction Ajouter des tailles de police personnalisées pour choisir des tailles de police adaptées à vos besoins.

Il y a une multitude de changements dans l'interface utilisateur que nous n'aborderons pas pour l'instant puisque nous allons présenter la chose la plus importante qui a changé dans PAGE 7.0. Je vais les mettre de côté, à vous de les trouver, et je pourrais en aborder certains dans de futurs articles. À mon avis, le plus grand changement est le moteur de création de code, qui prend les informations natives .tk du concepteur d'interface graphique et les transforme en code Python. Il a changé de façon significative. Don l'a pratiquement réécrit à partir de zéro.

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.

Lorsque vous créiez une interface graphique avec la version 6 de PAGE, toutes les variables Tk étaient visibles de votre programme par une fonction spéciale appelée set_Tk_var. Cela vous permettait de créer facilement des étiquettes dynamiques ou d'accéder et/ou de modifier divers attributs de widgets tels que le TCombobox par le biais de l'attribut textvariable (page suivante, en haut à droite).

Cette fonction a été créée par PAGE dans le module de support. Comme vous pouvez le voir dans l'extrait de code ci-dessus, si vous souhaitez modifier le texte de l'étiquette, il vous suffit d'utiliser la fonction

LabelDisplay.set()

pour modifier la valeur de ce que vous vouliez afficher.

Dans le module de support, PAGE 6 avait une fonction de démarrage appelée init qui était la toute dernière chose qui était exécutée avant que l'utilisateur ne voie le formulaire (en bas à gauche).

Chaque formulaire avait cette même fonction. Pour référencer directement un widget, nous devions utiliser w.widgetname pour accéder à ce widget. Cela ne s'appliquait pas aux variables Tk, seulement aux 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.

Comme PAGE 7 permet d'avoir plusieurs formulaires dans un module de code, il a fallu modifier le paradigme, car la possibilité de conflits de variables est devenue une préoccupation très réelle. Supposons que vous ayez deux formulaires ayant chacun un widget Label qui possède une variable texte appelée « LabelDisplay ». Si cela se produisait, vous ne pourriez pas être sûr que le texte du widget Label serait modifié. Afin d'éviter que cela ne se produise, Don a trouvé une façon très créative de l'éviter. Voici un programme simple à deux formulaires qui montre à quel point il est facile de s'en occuper (en bas à droite).

Les deux widgets Label ont le même nom de variable TK DynLabel, ce qui, bien que ce ne soit pas une très bonne pratique de programmation, est facile à faire sans réfléchir et, sous PAGE version 6 et inférieure, était tout à fait raisonnable. La version 7 a été améliorée pour vous permettre d'« emprunter » des formulaires et des widgets d'un projet antérieur qui comportait plusieurs formulaires. Tous les widgets et leurs variables peuvent être importés dans le nouveau projet. Afin d'éviter que cela ne pose problème, PAGE sépare chaque formulaire dans sa propre classe au sein du module GUI. À droite, vous trouverez un extrait rapide de ce que contient le GUI dans la 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.

Puisque chaque formulaire est maintenant défini comme une classe distincte, les noms des variables peuvent facilement être les mêmes entre les formulaires. Je vous montrerai (en haut à droite) comment on y accède dans un instant. Pour l'instant, jetons un coup d'oeil aux changements apportés au module de support. Il n'y a plus de fonction init, elle s'appelle maintenant main.

Le V7 dans le bout de code en haut à droite est le vrai nom du projet. Chaque formulaire a maintenant un alias qui commence par _w et ensuite un numéro pour faire référence au formulaire, donc _w1 est l'alias pour les widgets du formulaire 1 et _w2 fait référence à ceux du formulaire 2.

Pour modifier le texte de l'étiquette du premier formulaire, il suffit d'utiliser le code suivant :

_w1.DynLabel.set(“Texte”)

Et pour changer le texte de l'étiquette sur le deuxième formulaire, nous utiliserions le code suivant :

_w2.DynLabel.set(“Autre Texte”)

Pour faire référence au formulaire lui-même dans la version 6, nous utilisions root. qui nous permettait de faire des choses comme déplacer le formulaire sur l'écran ou terminer le programme. Dans la version 7, vous pouvez voir dans le code ci-dessus que root a été aliasé en _top1 pour le premier formulaire et _top2 pour le second. Il est important de se rappeler d'utiliser ce nouvel alias, car l'utilisation de root causera à nouveau des problèmes très inattendus (et souvent non désirés).

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.

Pour terminer le programme dans la version 6, vous utilisiez la fonction destroy_window() générée par PAGE. Dans la version 7, vous appelez simplement root.destroy(). L'appel de root à partir de votre code peut être problématique, car toutes les formes ont une racine. Cependant, l'appel de root.destroy() pour terminer le programme est correct, car il fermera toutes les fenêtres actives.

Lorsque vous exécutez un projet multiforme créé avec la version 7, vous pouvez être surpris de voir que tous les formulaires s'affichent immédiatement. Si vous regardez à nouveau le code, vous pouvez comprendre pourquoi. Tous les formulaires sont créés au démarrage et lorsque la ligne root.mainloop est exécutée, elle affiche tout ce qui a été défini. Parfois, c'est l'action souhaitée et c'est bien. Cependant, si vous avez une application pour laquelle vous voulez qu'un formulaire soit le formulaire principal et que les autres ne s'affichent que sur demande, ce n'est pas acceptable. Il est pourtant très facile de résoudre ce problème. Dans la version 6, j'ai créé deux petites fonctions dans le module de support que j'ai appelées show_me et hide_me. Lorsque mon formulaire principal devait afficher un autre formulaire, j'appelais la fonction hide_me pour minimiser le formulaire principal, puis j'appelais la fonction show_me pour afficher le second formulaire. Lorsque l'utilisation du second formulaire était terminée, j'appelais la fonction hide_me pour le second formulaire, puis la fonction show_me pour le formulaire principal. C'était un ensemble logique d'étapes pour le but recherché. Voici à quoi elles ressemblaient dans la 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_me():

  global root
  root.deiconify()

def hide_me():

  global root
  root.withdraw()

Nous ne pouvons pas utiliser ces fonctions directement dans la version 7, car elles appellent toutes deux directement l'objet racine. Cependant, il est simple de les modifier pour qu'elles fonctionnent correctement. Il suffit de créer un ensemble pour chaque formulaire :

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()

Vous pouvez même créer un seul ensemble de fonctions qui gère un nombre quelconque de formulaires en passant l'objet comme paramètre à la fonction qui définit le formulaire avec lequel vous voulez travailler :

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).

def show_form(which):

  which.deiconify()

def hide_form(which):

  which.withdraw()

Pour l'utiliser, nous devons créer une fonction de démarrage qui s'exécute juste avant l'affichage des formulaires et l'appeler juste avant la dernière ligne de la fonction principale.

Vous pouvez utiliser la fonction de démarrage (ou quel que soit le nom que vous lui donnez) pour faire des choses comme démarrer des bases de données ou initialiser divers paramètres et bibliothèques. Notre fonction de démarrage, dans ce cas, doit avoir au moins ceci :

def startup():

  hide_form(_top2)

Cela minimisera ou masquera le deuxième formulaire, laissant le premier formulaire (principal) visible. Afin d'afficher le second formulaire et de masquer le premier, nous avons besoin d'un bouton sur le formulaire principal pour déclencher les fonctions. Nous appellerons le bouton btnShow2 et définirons sa fonction de rappel comme on_btnShow2. Dans son callback, nous le coderons comme suit (en bas à droite).

(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!

(Les lignes qui ne sont pas en gras font partie du nouveau squelette de fonction de rappel que PAGE crée). Cela cache le premier formulaire et affiche le second. Ensuite, pour que la fonction de rappel restaure le premier formulaire et cache le second, nous faisons ce qui suit (en bas à gauche).

Super facile et propre. Tout ce dont vous devez vous souvenir est l'alias de formulaire que vous devez utiliser pour manipuler ce formulaire précis.

Je dois admettre qu'il m'a fallu un peu de temps avant de me sentir complètement à l'aise avec ces changements, mais ils ne sont pas aussi radicaux qu'il y paraît.

Il y a un certain nombre d'autres changements dans la version 7 de PAGE qui en améliorent vraiment les capacités, mais je ne vais pas tous les passer en revue maintenant. Si vous voulez avoir un moyen rapide et (assez) facile de créer des interfaces graphiques Tkinter pour vos programmes Python, vous devez vraiment obtenir la dernière version de PAGE. Vous pouvez la télécharger à partir de https://sourceforge.net/projects/page/

En raison de tous les changements que PAGE 7 apporte, j'ai écrit un nouveau tutoriel pour les nouveaux utilisateurs et un document qui montre un ensemble facile de conseils de migration pour ceux qui sont familiers avec les versions précédentes de PAGE. Les deux sont inclus dans chaque distribution de PAGE dans le dossier docs.

Jusqu'à la prochaine fois, comme toujours, restez en sécurité, en bonne santé, positif et créatif !

issue177/python.txt · Dernière modification : 2022/02/01 16:16 de andre_domenech