issue155:python
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
issue155:python [2020/04/03 08:23] – d52fr | issue155:python [2020/04/04 18:00] (Version actuelle) – andre_domenech | ||
---|---|---|---|
Ligne 3: | Ligne 3: | ||
As most of you know, I act as an unofficial support outlet for Page, the GUI designer for Python using Tkinter. On average, I spend probably 5 hours a week answering questions from users, both brand new to Page and users who have been using Page for years. I also help out Don Rozenberg with testing new development builds, trying to break it in as many ways as I can. Once I can’t break it anymore, Don usually creates a release. It’s a very time consuming job, but one I really enjoy.** | As most of you know, I act as an unofficial support outlet for Page, the GUI designer for Python using Tkinter. On average, I spend probably 5 hours a week answering questions from users, both brand new to Page and users who have been using Page for years. I also help out Don Rozenberg with testing new development builds, trying to break it in as many ways as I can. Once I can’t break it anymore, Don usually creates a release. It’s a very time consuming job, but one I really enjoy.** | ||
- | Une rapide mise à jour à propos de la bibliothèque recipe-scrapers dont j'ai parlé le mois dernier. Actuellement, | + | Une rapide mise à jour à propos de la bibliothèque recipe-scrapers dont j'ai parlé le mois dernier. Actuellement, |
- | Comme la plupart d' | + | Comme la plupart d' |
**Of all the questions I get about using Page, the one I get the most is how to create a program with multiple forms. The answer to this is really pretty easy, but is not as straight-forward as a user would think, hence the questions. The next question is how do I get the forms or windows to communicate with each other. My good friend Halvard from Norway asked how to have one form read information from another in “real time”. Again, while the answer is easy, it’s not something that most users would try before they ask the question. So, in this edition, I intend to present a very simple demo to help understand the process. AND there’s an added benefit in that this solution is not limited to a Page GUI program. It can be used for any Python program including CLI programs. Your imagination is the only limit. | **Of all the questions I get about using Page, the one I get the most is how to create a program with multiple forms. The answer to this is really pretty easy, but is not as straight-forward as a user would think, hence the questions. The next question is how do I get the forms or windows to communicate with each other. My good friend Halvard from Norway asked how to have one form read information from another in “real time”. Again, while the answer is easy, it’s not something that most users would try before they ask the question. So, in this edition, I intend to present a very simple demo to help understand the process. AND there’s an added benefit in that this solution is not limited to a Page GUI program. It can be used for any Python program including CLI programs. Your imagination is the only limit. | ||
Ligne 13: | Ligne 13: | ||
The project will consist of two Page forms, one called “Parent” and the other called “Child”. The Parent program will launch the Child program and will receive data from the child.** | The project will consist of two Page forms, one called “Parent” and the other called “Child”. The Parent program will launch the Child program and will receive data from the child.** | ||
- | Parmi toutes les questions que je reçois sur l' | + | Parmi toutes les questions que je reçois sur l' |
- | Aussi, j'ai pensé mettre sur pied une rapide démo en utilisant Page pour montrer comment | + | Aussi, j'ai pensé mettre sur pied une rapide démo en utilisant Page pour montrer comment |
- | Le projet sera constitué de deux formulaires | + | Le projet sera constitué de deux formulaires |
**Form Design | **Form Design | ||
Ligne 25: | Ligne 25: | ||
Below is what the Parent form looks like. I didn’t spend much time on making it pretty and mainly kept the attributes for everything on the form to their defaults where possible.** | Below is what the Parent form looks like. I didn’t spend much time on making it pretty and mainly kept the attributes for everything on the form to their defaults where possible.** | ||
- | Conception | + | Conception |
- | Nous créerons d' | + | Nous créerons d' |
Ci-dessous, voici à quoi ressemble le formulaire Parent. Je n'ai pas passé beaucoup de temps à l' | Ci-dessous, voici à quoi ressemble le formulaire Parent. Je n'ai pas passé beaucoup de temps à l' | ||
Ligne 35: | Ligne 35: | ||
btnLaunch has the command attribute set to “on_btnLaunch” and the btnExit has the command attribute set to “on_btnExit”. These are the names of the callback functions for each of the buttons. The only other in the top_level form is the title of “I am Parent”.** | btnLaunch has the command attribute set to “on_btnLaunch” and the btnExit has the command attribute set to “on_btnExit”. These are the names of the callback functions for each of the buttons. The only other in the top_level form is the title of “I am Parent”.** | ||
- | Comme vous pouvez le voir, le formulaire est très simple. Deux boutons et quatre étiquettes. Les deux boutons ont pour nom ou alias (de gauche à droite) btnLaunch et btnExit. Puis deux étiquettes statiques (status et received) et deux étiquettes dynamiques, l'un qui est un simple carré coloré qui montre l' | + | Comme vous pouvez le voir, le formulaire est très simple. Deux boutons et quatre étiquettes. Les deux boutons ont pour nom ou alias (de gauche à droite) btnLaunch et btnExit. Puis il y a deux étiquettes statiques (status et received) et deux étiquettes dynamiques |
- | btnLaunch a l' | + | btnLaunch a l' |
**Next, we’ll design our child program. This one is a bit more complicated, | **Next, we’ll design our child program. This one is a bit more complicated, | ||
Ligne 49: | Ligne 49: | ||
Ensuite, nous construirons notre programme enfant. Il est un peu plus compliqué, mais pas trop quand même. | Ensuite, nous construirons notre programme enfant. Il est un peu plus compliqué, mais pas trop quand même. | ||
- | Comme vous pouvez le voir, il y a un petit pavé numérique du genre « 10 touches » avec trois boutons annexes, le tout dans un cadre. Il y a aussi une étiquette qui affichera la valeur de la touche | + | Comme vous pouvez le voir, il y a un petit pavé numérique du genre « 10 touches » avec trois boutons annexes, le tout dans un cadre. Il y a aussi une étiquette qui affichera la valeur de la touche |
- | Les trois boutons complémentaires ont leurs attributs de commande | + | Les trois boutons complémentaires ont leurs attributs de commande |
- | Les 11 boutons du pavé n' | + | Les 11 boutons du pavé n' |
**The Communications Magic | **The Communications Magic | ||
Ligne 71: | Ligne 71: | ||
Import partagé | Import partagé | ||
- | Le fichier lui-même est en fait un fichier vide. Il n'y a rien dedans. Cependant, comme il est importé par nos deux programmes, | + | Le fichier lui-même est en fait un fichier vide. Il n'y a rien dedans. Cependant, comme il est importé par nos deux programmes, |
Malgré tout, vous devez rester prudent pour vous assurer que, avant d' | Malgré tout, vous devez rester prudent pour vous assurer que, avant d' | ||
Ligne 90: | Ligne 90: | ||
Le Code | Le Code | ||
- | Le code des deux modules _support.py sera présenté ci-dessous. Il est montré quasiment complet. Le code des fichiers d' | + | Le code des deux modules _support.py sera présenté ci-dessous. Il est montré quasiment complet. Le code des fichiers d' |
Comme toujours, nous commencerons par la section des imports pour le fichier parent_support.py. Notez que nous importons child.py, child_support.py et shared.py : | Comme toujours, nous commencerons par la section des imports pour le fichier parent_support.py. Notez que nous importons child.py, child_support.py et shared.py : | ||
Ligne 99: | Ligne 99: | ||
import shared | import shared | ||
- | La fonction suivante (ci-dessous) | + | La fonction suivante (ci-dessous) nous est fournie par Page ; c'est la fonction set_Tk_var. Elle nous donne accès à l' |
Ligne 106: | Ligne 106: | ||
The first two lines of my added code, set two variables in the shared module. That way, when the child program starts up, the variables are already there and can be written to when needed. We also use those variables in the next function so they need to be initialized right away. If we don’t, Python will throw an error.** | The first two lines of my added code, set two variables in the shared module. That way, when the child program starts up, the variables are already there and can be written to when needed. We also use those variables in the next function so they need to be initialized right away. If we don’t, Python will throw an error.** | ||
- | Ensuite (page suivante, en bas à gauche), voici la fonction init. C'est la toute dernière chose qui est lancée dans le programme avant que l' | + | Ensuite (page suivante, en bas à gauche), voici la fonction init. C'est la toute dernière chose qui est lancée dans le programme avant que l' |
- | Les deux premières lignes du code que j'ai ajouté paramètrent deux variables dans le module partagé. De cette façon, quand le programme enfant démarre, les variables sont déjà | + | Les deux premières lignes du code que j'ai ajouté paramètrent deux variables dans le module partagé. De cette façon, quand le programme enfant démarre, les variables sont déjà |
**The next thing we do is assign an alias for the status label, which is the red square that shows when the child program is running and connected to the shared module. Finally, I set up a timer function that Tkinter provides called “root.after”. This is an event that fires every X milliseconds to take care of just about any kind of repetitive task you want to do. You can also create multiple ‘root.after’ timers that run simultaneously. The basic syntax is: | **The next thing we do is assign an alias for the status label, which is the red square that shows when the child program is running and connected to the shared module. Finally, I set up a timer function that Tkinter provides called “root.after”. This is an event that fires every X milliseconds to take care of just about any kind of repetitive task you want to do. You can also create multiple ‘root.after’ timers that run simultaneously. The basic syntax is: | ||
Ligne 115: | Ligne 115: | ||
In the case above, the handle is called “comm1” which lets me know that this particular timer is used to communicate with the child process. Notice that I set the time to 0, which means that the callback function will be called immediately, | In the case above, the handle is called “comm1” which lets me know that this particular timer is used to communicate with the child process. Notice that I set the time to 0, which means that the callback function will be called immediately, | ||
+ | |||
+ | Ensuite, nous assignons un alias à l' | ||
+ | |||
+ | | ||
+ | |||
+ | Dans le cas ci-dessus, le handle est appelé « comm1 » ce qui vous fait savoir que ce compteur particulier est utilisé pour communiquer avec le traitement enfant. Notez que j'ai réglé le temps sur 0, ce qui signifie que la fonction de rappel est appelée immédiatement et le paramètre final est le nom de la fonction de rappel. | ||
**Bottom right is the callback function code… | **Bottom right is the callback function code… | ||
First, we set two global variables, comm1 which is the handle for the timer, and LblStat which is the alias for our little red square. Next, we access the shared module to see if the child process is running by checking shared.child_active to see if it’s True. This is set as soon as the child program starts up. If it is, we set the square colored label to “Green” to show that the child process is running and then we check to see if shared.ReadyToRead is set to True – which is basically a flag that says that one of the numeric keys has been clicked. If it is, we get that data, put it into the display label with the .set() method, and clear the ReadyToRead flag, so we can wait for the next click event on the child process. If the shared.child_active flag is False, we reset the coloured square to Red. This way, when the child program is exited, we will know it visually.** | First, we set two global variables, comm1 which is the handle for the timer, and LblStat which is the alias for our little red square. Next, we access the shared module to see if the child process is running by checking shared.child_active to see if it’s True. This is set as soon as the child program starts up. If it is, we set the square colored label to “Green” to show that the child process is running and then we check to see if shared.ReadyToRead is set to True – which is basically a flag that says that one of the numeric keys has been clicked. If it is, we get that data, put it into the display label with the .set() method, and clear the ReadyToRead flag, so we can wait for the next click event on the child process. If the shared.child_active flag is False, we reset the coloured square to Red. This way, when the child program is exited, we will know it visually.** | ||
+ | |||
+ | En bas à droite, voici le code de la fonction de rappel : | ||
+ | |||
+ | D' | ||
**Finally, we “re-arm” the timer routine, this time to check 100 ms from that point. | **Finally, we “re-arm” the timer routine, this time to check 100 ms from that point. | ||
Ligne 125: | Ligne 135: | ||
Next, we’ll look at the btnLaunch callback function (above). This is how we make the child program start. Again, we used the command attribute for the button in Page, so the skeleton is started for us…** | Next, we’ll look at the btnLaunch callback function (above). This is how we make the child program start. Again, we used the command attribute for the button in Page, so the skeleton is started for us…** | ||
+ | |||
+ | Enfin, nous « ré-armons » la routine du compteur, cette fois-ci pour décompter 100 ms à partir de là. | ||
+ | |||
+ | Maintenant, nous regardons la fonction de rappel on_btnExit (présentée ci-dessus). Elle est très simple. Nous appelons simplement la fonction destroy_window() qui terminera proprement le programme. La fonction destroy_window() est fournie par Page tout comme l' | ||
+ | |||
+ | Ensuite, nous regardons la fonction de rappel btnLaunch (ci-dessus). C'est par là que nous démarrons le programme enfant. À nouveau, nous utilisons l' | ||
**The only line we need to enter here is the last one. Since we have already imported the child.py GUI file at the top of the code, we just need to call the create_Toplevel1() function. This is the entry point for the program when it is called from another program. | **The only line we need to enter here is the last one. Since we have already imported the child.py GUI file at the top of the code, we just need to call the create_Toplevel1() function. This is the entry point for the program when it is called from another program. | ||
Ligne 131: | Ligne 147: | ||
Now we’ll look at the child program. It is a bit more complicated, | Now we’ll look at the child program. It is a bit more complicated, | ||
+ | |||
+ | La seule ligne que nous avons besoin de saisir ici est la dernière. Comme nous avons déjà importé le fichier d' | ||
+ | |||
+ | Enfin, j'ai mis destroy_window() (ci-dessous), | ||
+ | |||
+ | Maintenant, nous regardons le programme enfant. Il est un peut plus compliqué, mais pas trop. Ici encore, je vais fournir le code uniquement pour le module child_support.py. | ||
**Again, we’ll start with the import section. Notice here, that we need to import only the shared module, since we don’t need to call any functions from the parent. | **Again, we’ll start with the import section. Notice here, that we need to import only the shared module, since we don’t need to call any functions from the parent. | ||
Ligne 141: | Ligne 163: | ||
The first thing we do is set up a global variable to hold the accumulated value of the keypad entries. We then call the function setup_bindings() that attaches the callback function to all of the buttons of the keypad. Finally, we set the shared.child_active flag to True.** | The first thing we do is set up a global variable to hold the accumulated value of the keypad entries. We then call the function setup_bindings() that attaches the callback function to all of the buttons of the keypad. Finally, we set the shared.child_active flag to True.** | ||
+ | |||
+ | À nouveau, nous commençons par la section des imports. Notez ici que nous ne devons importer que le module partagé, car nous n' | ||
+ | |||
+ | import sys | ||
+ | |||
+ | import shared | ||
+ | |||
+ | En haut à droite, voici la définition de l' | ||
+ | |||
+ | La première chose que nous faisons est de paramétrer une variable globale pour garder la valeur cumulative des saisies du pavé. Ensuite, nous appelons la fonction setup_bindings() qui relie la fonction de rappel à tous les boutons du pavé. Enfin, nous paramétrons le drapeau shared.child_active à True. | ||
**Since we are going to pass parameters to the callback for the keypad buttons, it’s much easier to deal with things here (below) than to try to do it within Page through the command attribute. | **Since we are going to pass parameters to the callback for the keypad buttons, it’s much easier to deal with things here (below) than to try to do it within Page through the command attribute. | ||
Ligne 147: | Ligne 179: | ||
In the callback, we simply take the value (which is the number of the button) and append it, as a string, to the valu variable. We also check to see if the period key (value 10) was pressed, and if it was, then we add the period into the display value. Finally, we put the data into the DisplayLabel through the .set() method, and set shared.ReadyToRead to True, so the parent knows to pull the data.** | In the callback, we simply take the value (which is the number of the button) and append it, as a string, to the valu variable. We also check to see if the period key (value 10) was pressed, and if it was, then we add the period into the display value. Finally, we put the data into the DisplayLabel through the .set() method, and set shared.ReadyToRead to True, so the parent knows to pull the data.** | ||
+ | |||
+ | Comme nous allons passer des paramètres à la fonction de rappel des boutons du pavé, il est plus facile de s' | ||
+ | |||
+ | Maintenant, nous définissons le code de la routine de rappel (page suivante, en haut à gauche) dans le cas où on aurait cliqué sur un bouton du pavé. Notez qu'il faut le faire à partir de zéro, car Page n'a aucune idée de la nécessité de cette fonction. | ||
+ | |||
+ | Dans la fonction de rappel, nous prenons simplement la valeur (qui est le numéro du bouton) et le rajoutons, sous forme de chaîne, à la variable valu. Nous vérifions aussi si la touche du point (valeur 10) a été enfoncée, et, si c'est le cas, nous ajoutons le point à la valeur affichée. Enfin, nous mettons les données dans DisplayLabel par la méthode .set(), et réglons shared.ReadyToRead à True, de sorte que le parent sait qu'il doit récupérer les données. | ||
**We don’t do anything with the Enter button, so we just leave the skeleton for later use (far right). | **We don’t do anything with the Enter button, so we just leave the skeleton for later use (far right). | ||
Ligne 163: | Ligne 201: | ||
Until next time, keep coding.** | Until next time, keep coding.** | ||
+ | |||
+ | Nous ne faisons rien du bouton Entrée ; aussi, nous gardons juste la structure pour un usage ultérieur (à droite, en haut). | ||
+ | |||
+ | Comme d' | ||
+ | |||
+ | parent.py - https:// | ||
+ | |||
+ | parent_support.py - https:// | ||
+ | |||
+ | child.py - https:// | ||
+ | |||
+ | child_support.py - https:// | ||
+ | |||
+ | J' | ||
+ | |||
+ | Jusqu' | ||
+ | |||
+ | p 26, col 4, encart, lignes noires : | ||
+ | |||
+ | **Here is the init function from the child program. Again, you can see where my code starts.** | ||
+ | |||
+ | Voici la fonction init du programme enfant. À nouveau, vous pouvez voir où commence mon code. | ||
+ | |||
+ | p 25, col 4, encart, 3 § noirs : | ||
+ | |||
+ | **The callback for the Exit button is mostly the same as for the parent, but we also set shared.child_active to False, so the Parent knows to change the status square from Green to Red and not to try to poll.** | ||
+ | |||
+ | La fonction de rappel du bouton Exit est principalement la même que pour le parent, mais nous réglons aussi shared.child_active à False, de sorte que le parent sait qu'il doit changer le carré du vert au rouge et ne plus essayer de l' | ||
+ | |||
+ | **The btnClear callback function sets the global valu to an empty string, sets data into the shared module and the display label, and then sets the shared.ReadyToRead flag to True.** | ||
+ | |||
+ | La fonction de rappel btnClear charge la globale valu avec une chaîne vide, règle les données dans le module partagé et l' | ||
+ | |||
+ | **Finally, we deal with the callback for the Backspace button. We simply strip the last character from the valu string, display it, pass it to the shared module, and set the flag so that the parent program will read it.** | ||
+ | |||
+ | Enfin, nous nous occupons de la fonction de rappel du bouton Backspace (Retour Arrière). Nous supprimons juste le dernier caractère de la chaîne valu, l' | ||
+ |
issue155/python.1585894995.txt.gz · Dernière modification : 2020/04/03 08:23 de d52fr