Outils pour utilisateurs

Outils du site


issue149:python

Occasionally, I help Don Rozenberg, the creator of Page (a GUI designer for Python) with testing, demos, tutorials and support. This week, we had a user who was asking about an issue with the ttk themed widgets. Don passed the question down to me and I had to scramble since it’s been a long time since I’ve used the ttk specific widgets. If you are unfamiliar with ttk widgets, it is an additional set of widgets available for Tkinter from the Tk toolkit, which is part of Tcl. The Tk toolkit is available for Perl and Ruby as well as Tcl and Python. The ttk portion of the toolkit gives alternate widgets for many of the standard Tk widgets…

De temps en temps, j'aide Don Rozenberg, le créateur de Page (un concepteur d'interface graphique « GUI » en Python) en faisant des tests, des démos, des tutoriels et du support. Cette semaine, j'ai eu un utilisateur qui avait un problème avec les gadgets à thème de ttk. Don m'a passé la question et j'ai dû batailler, car je n'avais pas utilisé les gadgets spécifiques de ttk depuis longtemps.

Si vous ne connaissez pas les gadgets de ttk, c'est un ensemble de gadgets complémentaires disponibles pour Tkinter dans la boîte à outils Tk, qui fait partie de Tcl. La boîte à outils Tk est disponible pour Perl et Ruby ainsi que pour Tcl et Python. La portion pour ttk de la boîte à outils donne des gadgets de remplacement pour beaucoup de gadgets Tk standards…

They pretty much act as the standard widgets do, they just have a different, more updated look to them (at least to some people). There are also preset styles that you can use that changes the look of them. On Linux machines, there are 4 pre-defined styles, named clam, alt, default and classic). On Windows machines, there are three others as well, called “winnative’, ‘vista’ and ‘xpnative’. And Mac users have other choices, but the bottom line there are on all platforms, the 4 base styles that are on Linux. When I am making a UI, I usually shy away from the ttk widgets for the most part because they have less “visible” attributes than the standard widgets and I feel like I lose some control over my program. Here is an example using Page’s attribute editor…

Ils fonctionnent de façon assez similaire aux gadgets standards ; ils ont surtout un aspect différent, plus à jour (du moins selon certaines personnes). Ils disposent aussi de thèmes pré-réglés que vous pouvez utiliser pour changer leur aspect. Sur les machines Linux, il y a 4 styles prédéfinis, appelés clam, alt, default et classic). Sur les machines Windows, il y en a aussi trois autres, appelés « winnative », « vista » et « xpnative ». Et les utilisateurs de MAC ont d'autres choix, mais les 4 styles de base sous Linux sont le minimum disponible sur toutes les machines.

Quand je fais une interface utilisateur, j'évite habituellement les gadgets ttk dans l'ensemble, parce qu'ils ont moins d'attributs « visibles » que les gadgets standards et j'ai l'impression de perdre la maîtrise de mon programme. Voici un exemple utilisant l'éditeur d'attribut de Page :

I know this graphic is mostly unreadable, but it is illustrative of the number of attributes that you can control for a standard button (on the left side) compared to the ttk button (on the right). Part of the discrepancy stems from the fact that the style controls most of the attributes that seem to be missing. In a good way, this is a good thing. On the other hand, since the style is “taking care of these attributes for you”, for the most part the style keeps you from making changes to what could normally be an important attribute when you are trying to “push the envelope” and make your GUI program look more modern than standard Tkinter widgets allow. While the above might dissuade many users from even trying ttk widgets, a larger number of users are overwhelmed by how hard it is to find really good documentation on the use and customization of ttk widgets. Even those sites on the Internet that touch on how to do it, only give a small amount of information and even then, there isn’t much about using ttk goodies in Page. So, I thought I’d try to fix this in some small way.

Je sais que cette image est à peu près illisible, mais elle illustre bien le nombre d'attributs que vous pouvez contrôler pour un bouton ordinaire (sur le côté gauche) par rapport à un bouton ttk (à droite). Une partie de la différence vient du fait que le style contrôle la plupart des attributs qui semblent manquer. D'une certaine manière, c'est une bonne chose, car le style « s'occupe des attributs à votre place » ; majoritairement, le style vous empêche de faire des modifications à ce qui pourrait être un attribut important quand vous essayez de « gagner de la place » pour donner à votre programme d'interface un aspect plus moderne que ce qu'autorisent les gadgets Tkinter standards.

Bien que cela puisse dissuader certains utilisateurs de donner une chance aux gadgets ttk, un grand nombre d'utilisateurs sont dépassés devant la difficulté de trouver une bonne documentation sur l'utilisation et la personnalisation des gadgets ttk. Même sur les sites d'Internet qui parlent de comment faire, la quantité d'information fournie est faible et, même là, il n'y a pas grand chose sur l'utilisation des petits trucs en ttk dans Page. Aussi, je pensais que j'allais essayer de résoudre le problème au moins un peu.

One of the problems with using ttk widgets in Page is that Page has (at least currently) no way to set which of the styles you want to use either when you are designing your GUI and to make it work when you try to run your program shows the ‘default’ style at least under Linux. I believe that Windows users have the ‘winnative’ style as the default. While it is easy to change/setup the style in the startup function ( def init() ), a casual user who’s done no serious research on the subject would look at the results and say “We’ll, that doesn’t look like what I wanted!” and go back to using the standard widgets. Moreover, there could be issues when doing cross platform GUI design. Since Linux doesn’t support the ‘winnative’ style, there could be issues at best (the program uses the default style) and at worst, simply throws error messages that, again, for those who haven’t spent a fair amount of time researching would be meaningless.

Un des problèmes lors de l'utilisation des gadgets ttk dans Page est que Page n'a (au moins actuellement) aucune façon de régler le style que vous voulez utiliser quand vous concevez votre GUI et le faire fonctionner quand vous essayez de lancer votre programme affiche le style par défaut (« default »), au moins sous Linux. Je crois que les utilisateurs de Windows ont par défaut le style « winnative ». Alors qu'il est facile de modifier le style dans la fonction de démarrage ( def init() ), un utilisateur ordinaire qui ne fait pas de recherches sérieuses sur le sujet regardera les résultats en disant « Beh ! Ça ne ressemble pas à ce que je voulais ! » et reviendra à l'utilisation des gadgets standards. De plus, il pourrait y avoir des problèmes lors d'une conception multi-plateforme. Comme Linux ne supporte pas le style « winnative », il pourrait au mieux y avoir des problèmes (le programme utilise le style par défaut) et, au pire, des messages d'erreur qui, à nouveau pour ceux qui n'ont pas passé un bon bout de temps à faire des recherches, n'ont aucun sens.

So we’ll try to get a handle on some of the issues and get the hard stuff out of the way in this article. The latest version of Page is 4.25.1 and you can get the latest at https://sourceforge.net/ projects/page/ . Once you have it installed, get it fired up and we’ll throw together a quick demo. Start by making your new form about 516 pixels wide by 450 high. Now set the title to something like “ttk Widget Demo”. Next, add a TButton widget to act as our Exit button. Put it in the upper right corner of the form. Set the command attribute to “on_btnExit” and change the text attribute to “Exit”.

Aussi, dans cet article, nous allons essayer de résoudre quelques problèmes et nous débarrasser des difficultés. La dernière version de Page est la 4.25.1 et vous pouvez obtenir la plus récente sur https://sourceforge.net/projects/page/. Une fois installée, lancez-la et nous allons créer ensemble une démo rapide.

Commencez par changer le format en 516 pixels de large par 450 de haut. Maintenant réglez le titre avec quelque chose comme « Démo d'un gadget ttk ».

Ensuite, ajoutez un gadget TButton pour qu'il marche comme un bouton Exit (Quitter). Mettez-le dans le coin en haut à droite de la surface. Paramétrez l'attribut de commande sur « on_btnExit » et modifiez l'attribut de texte pour « Exit ».

Now, place a TLabel frame below the Exit button and size it to about 150 by 285. This will hold 7 TRadiobuttons. Set the Text attribute to “Styles”. Next add 7 TRadiobuttons into the TLabelframe. They should be aliased to “TRadiobutton1” to “TRadiobutton7”. This is important because we will reference them directly when the program runs. For each of the buttons, you need to set a few attributes. In the command attribute box, enter “on_rbClick”. It is important that each TRadiobutton references the same callback. Then set the value attribute to the count of that widget minus 1. So, for example, for TRadiobutton1, the value would be 0, TRadiobutton2 would be 1, TRadiobutton3 would be 2 and so on. Finally, for the TRadiobuttons, simply make sure that the Variable attribute reads “::selectedButton”, again the same for all 7. I used a vertical separation of 30 pixels between each one and lined them all with an X position of 20.

Maintenant, placez un cadre TLabel sous le bouton Exit et dimensionnez-le à 150 par 285. Celui-ci contiendra 7 TRadiobuttons (boutons radios). Réglez l'attribut de texte sur « Styles ». Ensuite, ajoutez 7 TRadiobuttons dans le Tlabelframe. Ils seront appelés « TRadiobutton1 » à « TRadiobutton7 ». C'est important, parce qu'il y sera fait référence directement quand le programme tournera. Pour chaque bouton, vous devrez régler quelques attributs. Dans le champ de l'attribut de commande, entrez « on_rbClick ». C'est important que tous les TRadiobuttons fassent référence au même appel. Ensuite, réglez l'attribut de valeur sur le numéro du gadget moins 1. Ainsi, par exemple, pour TRadiobutton1, la valeur sera 0, 1 pour TRadiobutton2, 2 pour TRadiobutton3 et ainsi de suite. Enfin, pour les TRadiobuttons, assurez-vous simplement que l'attribut Variable se lit : « ::selectedButton » pour chacun des 7. J'ai utilisé une séparation verticale de 30 pixels entre chaque et les ai tous alignés avec une position en X de 20.

We are over half way done. Now you need to put a TCheckbutton, a TCombobox, a TProgressbar and a TEntry widget in the form on the left side of the form. You can space it anyway you want. Finally, if you want, you can add a vertical and horizontal TSeparator for some decoration. You can see what my final form looks like in the image at the top of this article. Save the Page file as “ttkdemo.tcl”, then generate both of the Python modules. They will be saved as “ttkdemo.py” and “ttkdemo_support.py” automatically by Page. You can close Page now. Now for the code. In your favorite text editor or IDE, open the “ttkdemo_support.py” file. That's where all of our code will be entered.

Nous avons fait la moitié du chemin. Maintenant, nous avons besoin de mettre un TCheckbutton, un TCombobox, un TProgressbar et un gadget TEntry dans la partie gauche du cadre. Vous pouvez les espacer comme vous voulez. Enfin, si vous le voulez, vous pouvez ajouter des Tseparator vertical et horizontal pour décorer. Vous pouvez voir à quoi ressemble le résultat final dans l'image au début de l'article.

Sauvegardez le fichier Page comme « ttkdemo.tcl », puis générez les deux modules Python. Il seront enregistrés automatiquement par Page sous les noms « ttkdemo.py » et « ttkdemo_support.py ». Vous pouvez fermer Page maintenant.

Maintenant, le code. Dans votre éditeur de texte favori ou dans votre environnement de développement (IDE), ouvrez le fichier « ttkdemo_support.py ». C'est là que tout le code sera saisi.

If everything went correctly during the GUI design process, Page should have created the following functions for you… set_Tk_var() on_rbClick() on_btnExit() init() destroy_window() and the if name routine. Our code will be pretty minimal. We'll start with fleshing out the on_rbClick() function. This is a callback function that fires whenever the user clicks on one of the TRadiobuttons. Code is shown on the next page, top right.

Si tout s'est bien déroulé pendant la création du GUI, Page devrait avoir créé pour vous les fonctions suivantes :

set_Tk_var() on_rbClick() on_btnExit() init() destroy_window() et la routine if name

Notre code sera assez réduit. nous commencerons par mettre au point la fonction on_rbClick(). C'est une fonction d'appel qui se déclenche quand l'utilisateur clique sur l'un des TRadiobuttons. Le code est présenté sur la page suivante, en haut à droite.

Page put the first two statements (print(…) and sys.stdout.flush() in for you. So the first line of our code prints the value of the TRadiobutton that we entered in the 'value' attribute of the widget when we were designing the form in Page. This way the program knows which button was clicked on. The next line gets the styles that ttk supports for this operating system. In Linux, there will be 4 items and on Windows there will be 7 and they will be returned as a tuple, like this… ('clam', 'alt', 'default', 'classic') Next we repeat the operation in the print statement that gets the value of that TRadiobutton and finally, we tell ttk to use the correct style to draw the widgets. This happens immediately.

Page a incorporé les deux premières déclarations (print(…) et sys.stdout.flush()) à votre place. Ainsi, la première ligne de notre code imprime la valeur du TRadiobutton que nous avons entrée dans l'attribut « value » du gadget quand nous faisions la mise en forme dans Page. De cette façon, le programme connaît quel bouton a été cliqué. La ligne suivante obtient les styles que ttk supporte dans ce système d'exploitation. Dans Linux, il y a quatre éléments et dans Windows, il y en a 7 ; ils sont retournés comme un tuple, comme ceci :

('clam', 'alt', 'default', 'classic')

Ensuite, nous répétons l'opération dans la déclaration print qui obtient la valeur du TRadiobutton et, enfin, nous disons a ttk d'utiliser le style correct pour dessiner les gadgets. C'est fait immédiatement.

Now we'll put in the code for the on_btnExit() callback… def on_btnExit(): print('ttkdemo_support.on_btnExit') sys.stdout.flush() destroy_window() We'll add just one line to the bottom of the init function, which if you remember, is the very last thing that gets run before the form is shown to the user and the program actually starts. This will call the routine that will do all of our setup on the form. setup_styles()

Maintenant, nous incorporons l'appel on_btnExit() dans le code : def on_btnExit():

      print('ttkdemo_support.on_btnExit')
  

sys.stdout.flush()

destroy_window()

Nous ajouterons juste une ligne en bas de la fonction init, qui, si vous vous en souvenez, est le tout dernier truc qui est lancé avant que le formulaire ne soit présenté à l'utilisateur et que le programme démarre vraiment. Elle appelera la routine qui fera tout le paramétrage de notre formulaire.

setup_styles()

This is the longest and some would say the most complicated function in the program. It is also the only one that we have to create fully by hand. I'll break up the function in parts (see above). So first, we have the function definition and we create a list that consists of the 7 TRadiobuttons that we entered. We use their names directly and preface each with a “w.”. By doing this, we can reference any of the 7 widgets directly. s = ttk.Style() cntr = 0 The next two lines, create an object named “s” that holds all of the style information from ttk. We'll only be using one part of it, but once you get more familiar with ttk, this gives you access to many things. We also create and initialize a counter variable. def clear_radio_buttons(): for i in range(7): rblist[i].configure(text=) rblist[i].configure(state='disabled') C'est la fonction la plus longue et, certains dirons, la plus compliquée du programme. C'est aussi la seule que nous devons créer à la main. J'éclate cette fonction en plusieurs parties (voir ci-dessus). D'abord, nous avons la définition de la fonction et nous créons une liste constituée des 7TRadiobuttons que nous avons entrés. Nous utilisons leurs noms directement en préfaçons chacun avec un « w. » En ce faisant, nous pouvons faire référence directement à n'importe lequel des 7 gadgets. s = ttk.Style() cntr = 0 Les deux lignes suivantes créent un objet nommé « s » qui contient les informations de style venant de ttk. Nous n'en utiliserons qu'une partie, mais une fois que vous serez plus à l'aise avec ttk, cela vous donnera accès à beaucoup de choses. Nous créons aussi une variable compteur et l'initialisons. def clear_radio_buttons(): for i in range(7): rblist[i].configure(text='') rblist[i].configure(state='disabled') Now we create a function within our function that clears the text field and disables all 7 of the radio buttons. I'm sure you realize why we clear the text fields, and the reason we disable all the TRadiobutton widgets at this point is so that if we are running under Linux, the user can't click buttons 5 through 7, but if we are under Windows, all 7 will be activated in the next bit of code. Welcome to cross-platform programming! clear_radio_buttons() Now we call our in-function function that sets up the TRadiobuttons and we are ready to “load” them all up. for i in s.theme_names(): rblist[cntr].configure(text=i) rblist[cntr].configure(state='normal') cntr += 1 Maintenant, nous créons une fonction dans notre fonction qui efface les champs de texte et désactive les 7 boutons radio. Je suis sûr que vous comprenez pourquoi nous effaçons les champs de texte, et la raison pour laquelle nous désactivons tous les boutons gadgets TRadiobuttons à cet endroit vient du fait que, si nous fonctionnons sous Linux, l'utilisateur ne peut pas cliquer les boutons 5 à 7, alors que si nous sommes sous Windows, les 7 boutons seront tous activés dans le prochain bout de code. Bienvenue dans la programmation multi-plateforme ! clear_radio_buttons() Nous appelons maintenant la fonction dans la fonction qui paramètre les TRadiobuttons et nous sommes prêts pour tous les « charger ». for i in s.theme_names(): rblist[cntr].configure(text=i) rblist[cntr].configure(state='normal') cntr += 1 We'll use a simple for loop to do this. We will again, use the list that we created of all the TRadiobutton widgets to get the actual widget object name, set it's text to the style name for that position in the loop and set the state back to “normal”. Finally, we'll set the TProgress bar so it does something. The progress bar has two modes, 'determinate' for when you know how far into a process you are and you want to show a percentage of that progress. The 'indeterminate' mode simply causes a bar to move back and forth to show something is happening. You use the ‘.start()’ method to begin the motion and the ‘.stop()’ method to stop it. We'll use the 'indeterminate' method just for fun. w.TProgressbar1.config(mode='indeterminate') w.TProgressbar1.start() Nous utiliserons une simple boucle for pour le faire. Nous utiliserons, à nouveau, la liste que nous avons créée de tous les gadgets TRadiobutton pour obtenir le vrai nom d'objet du gadget, paramétrerons son texte suivant le nom du style pour cette place dans la boucle et reparamétrerons l'état à « normal ». Enfin, nous paramétrerons la barre TProgress pour qu'elle fasse quelque chose. La barre de progression a deux modes, « determinate » (déterminé) quand vous savez où vous en êtes dans le déroulement du processus et que vous voulez montrer un pourcentage de cet avancement. Le mode « indeterminate » (indéterminé) amène la barre à simplement avancer/reculer pour montrer que quelque chose se passe. Vous utilisez la méthode « .start() » pour commencer le mouvement et la méthode « .stop() » pour l'arrêter. Nous utiliserons la méthode « indeterminate » juste pour le plaisir. w.TProgressbar1.config(mode='indeterminate') w.TProgressbar1.start() That's it. Save your program and you can run it in Python. Now, anytime you want to use ttk widgets in your GUI, you know what they will look like under any style and once you have selected one to your liking, you can put somewhere in the init() function (after the first four lines) the following lines… style = ttk.Style() style.theme_use(‘your_style_here’) That was easy and pretty much painless. As always, I've put all the code up on pastebin at the following links… Ça y est. Sauvegardez votre programme et vous pouvez le lancer dans Python. Maintenant, chaque fois que vous voulez utiliser des gadgets ttk dans votre GUI, vous savez à quoi ils ressembleront quel que soit le style et une fois que vous en avez choisi un à votre goût, vous pouvez mettre les lignes suivantes quelque part dans la fonction init() (après les quatre premières lignes) : style = ttk.Style() style.theme_use(‘votre_style_ici’) C'était facile et pratiquement sans difficulté. Comme toujours, j'ai mis le code sur pastebin aux liens suivants : ttkdemo.tcl - https://pastebin.com/yFnH6QXF ttkdemo.py - https://pastebin.com/BWV1CxWN ttkdemo_support.py - https://pastebin.com/N72NXsUC When you download them, they will most likely come down as “ttkdemo.tcl.txt”, “ttkdemo.py.txt” and so on. That’s just a pastebin thing. Simply delete the “.txt” from the files and you’re good to go. Until next time, have a great month and keep programming!** ttkdemo.tcl - https://pastebin.com/yFnH6QXF ttkdemo.py - https://pastebin.com/BWV1CxWN ttkdemo_support.py - https://pastebin.com/N72NXsUC Quand vous les téléchargerez, ils arriveront sous les noms « ttkdemo.tcl.txt », « ttkdemo.py.txt » et ainsi de suite. C'est juste un truc de pastebin. Effacez simplement le « .txt » des fichiers et vous pouvez y aller. Jusqu'à la prochaine fois, passez un bon mois et continuez à programmer !

issue149/python.txt · Dernière modification : 2019/10/10 16:14 de andre_domenech