Outils pour utilisateurs

Outils du site


issue175:python

A few weeks ago, one of my Python newsfeeds alerted me about a neat new project – one that creates a still video image that is downloaded from NASA (National Aeronautics and Space Administration) every 45 seconds or so and displays it in the application’s GUI window. I thought that was a neat idea, so I jumped to the website and started reading. It seems that the author decided to use PySimpleGUI to make the GUI form, which is a program that I’m not really familiar with. I took a look at his screenshot of the GUI and I thought that I could create a comparable version using PAGE. I took up the challenge and thought that it would be a good project to present here. So I present my version of Spacestills.py. The author’s blog is at https://blog.paoloamoroso.com/2021/04/a-nasa-tv-still-frame-viewer-in-python.html, and is nicely presented. His source code can be found at https://github.com/pamoroso/spacestills.

Il y a quelques semaines, l'un de mes fils d'actualité Python m'a signalé un nouveau projet intéressant, qui crée une image vidéo fixe téléchargée depuis la NASA (National Aeronautics and Space Administration) toutes les 45 secondes environ et l'affiche dans la fenêtre de l'interface graphique de l'application.

J'ai pensé que c'était une bonne idée, alors j'ai sauté sur le site Web et commencé à lire. Il semble que l'auteur ait décidé d'utiliser PySimpleGUI pour créer le formulaire GUI, un programme que je ne connais pas vraiment. J'ai jeté un coup d'œil à sa capture d'écran de l'interface graphique et j'ai pensé que je pourrais créer une version comparable en utilisant PAGE. J'ai relevé le défi et j'ai pensé que ce serait un bon projet à présenter ici. Je vous présente donc ma version de Spacestills.py.

Le blog de l'auteur est à https://blog.paoloamoroso.com/2021/04/a-nasa-tv-still-frame-viewer-in-python.html, et est joliment présenté. Son code source peut être trouvé à https://github.com/pamoroso/spacestills.

We’ll be using two external Python libraries. You might already have them. They are requests and PIL (pillow). If you don’t have them, you can use pip to install them. They have to be on your system before you try to run the project. Here is what my version of his project looks like during a running session. I decided to make a little bit more space between the rows of user accessible widgets, since my mousing hand has a tendency to shake a bit. We’ll start to create the GUI in PAGE. Just in case you don’t have PAGE, you can get it at https://sourceforge.net/projects/page/. The installation details are in the documentation or in various past Full Circle articles of mine. You should use Python 3.6 or higher, since I use f-strings for some of the print statements.

Nous utiliserons deux bibliothèques Python externes. Vous les avez peut-être déjà. Il s'agit de requests et PIL (pillow). Si vous ne les avez pas, vous pouvez utiliser pip pour les installer. Elles doivent être sur votre système avant que vous n'essayiez d'exécuter le projet.

Voici à quoi ressemble ma version de son projet pendant une session qui tourne.

J'ai décidé de faire un peu plus d'espace entre les rangées de widgets accessibles à l'utilisateur, car la main que j'utilise pour la souris a tendance à trembler un peu. Nous allons commencer à créer l'interface graphique en PAGE.

Au cas où vous n'auriez pas PAGE, vous pouvez l'obtenir sur https://sourceforge.net/projects/page/. Les détails de l'installation se trouvent dans la documentation ou dans plusieurs de mes anciens articles de Full Circle. Vous devriez utiliser Python 3.6 ou une version plus récente, car j'utilise des f-strings pour certaines des instructions d'impression.

Let’s look at some of the requirements that we need to keep in mind during the design process. The images that come in from the Internet will be 704×480 pixels. The shortest time between the images is 45 seconds. This information came from the author's blog page as well as his code (his blog page states the image size is 704×408, but the actual image and his code is 704×480). The only code of his that I used are a few of his constants. The rest of the code for this project is code that I have used in previous projects. His project allows the end user to select the time between requests to be between 45 seconds and 300 seconds. He also allows the ability to change the aspect ratio from 704×480 to a 16×9 format which means that the image will be resized to 704×396 before it is displayed and/or saved. There is a Checkbutton that allows for this resize to be performed. The author has a Save button that allows the image to be saved to the hard drive, as well as a Reload button. This is helpful if you change between the native image size to the 16×9 format (or if you are impatient and don’t want to wait for the next refresh to occur). He also provides a Checkbutton to change the delay time between requests, and a text entry widget (and a button) to set the new time. I decided to use a Tk::Spinbox to make the time selection easier and doesn’t require the Set button. I do, however, include the set button, but all it does is bring up a warning Message box. The reason that I decided to use the Spinbox is that if you use the Up/Down arrows on the Spinbox, you don’t have to check if the value is numeric, keeping the code simpler.

Examinons quelques-unes des exigences que nous devons garder à l'esprit pendant le processus de conception. Les images provenant d'Internet auront une taille de 704 x 480 pixels. Le temps le plus court entre les images est de 45 secondes. Ces informations proviennent de la page du blog de l'auteur ainsi que de son code (sa page de blog indique que la taille de l'image est de 704 x 408, mais l'image réelle et son code sont de 704 x 480). De son code, je n'ai utilisé que certaines de ses constantes. Le reste du code pour ce projet est du code que j'ai utilisé dans des projets précédents. Son projet permet à l'utilisateur final de sélectionner un délai entre les requêtes de 45 secondes et 300 secondes. Il permet également de modifier le rapport d'aspect de 704×480 à un format 16:9, ce qui signifie que l'image sera redimensionnée à 704 x 396 avant d'être affichée et/ou sauvegardée. Il y a un bouton de contrôle qui permet d'effectuer ce redimensionnement. L'auteur dispose d'un bouton Enregistrer qui permet de sauvegarder l'image sur le disque dur, ainsi que d'un bouton Recharger. Ce dernier est utile si vous passez de la taille native de l'image au format 16:9 (ou si vous êtes impatient et ne voulez pas attendre le prochain rafraîchissement). Il fournit également un bouton de contrôle pour modifier le délai entre les requêtes, et un widget de saisie de texte (et un bouton) pour définir le nouveau délai. J'ai décidé d'utiliser une Tk::Spinbox pour faciliter la sélection du temps et le bouton Set n'est pas nécessaire. J'inclus cependant le bouton Set, mais il ne fait qu'afficher un message d'avertissement. La raison pour laquelle j'ai décidé d'utiliser la Spinbox est que si vous utilisez les flèches Haut/Bas de la Spinbox, vous n'avez pas besoin de vérifier si la valeur est numérique, ce qui simplifie le code.

So start up PAGE, and resize the default Toplevel designer window to about 777 pixels wide and 657 high. Don’t be too concerned about the actual dimensions of the main form, just get it close. Now move the form to somewhere in the middle of your screen. Set the title attribute in the Attribute Editor to “NasaStills-tk”. While we are in the Attribute Editor, set the background color to “skyblue4”. This is a nice dark bluish gray color. Next, we’ll put a frame widget into the Toplevel form. This will hold the Label widget that we will use to display the still image from the Nasa site. Place it near the top-left of the main form. Don’t worry too much about the placement at this point. Using the Attribute Editor again, make sure that the frame is selected, and set the width to 714 and the height to 492. Next, add a Label widget and set the X and Y position in the Attribute editor to 2 and 2. This will provide a nice little border around the Label widget when it is set to its full size. Set the width of the Label to 704, the height to 488, and the background color to “skyblue3”. This gives us a nice color for the visual inner display area. Set the Alias of the Label widget to “labelImage”, and delete the text in the text attribute. Now we will finish the placement of the Frame and Label combo. PAGE has a feature that allows you to select multiple widgets and manipulate them as a group, like centering horizontally or vertically, equally spacing them within their parent, and so on. At this point, we want to just center the frame within the main form horizontally. In the Widget Tree, use the middle mouse button to click on the Frame: Frame1 entry.

Lancez donc PAGE, et redimensionnez la fenêtre par défaut du concepteur Toplevel à environ 777 pixels de large et 657 de haut. Ne vous préoccupez pas trop des dimensions réelles du formulaire principal, il suffit qu'elles soient proches. Déplacez maintenant le formulaire au milieu de votre écran. Définissez « NasaStills-tk » pour l'attribut title dans l'éditeur d'attributs. Pendant que vous êtes dans l'éditeur d'attributs, définissez la couleur d'arrière-plan sur « skyblue4 ». Il s'agit d'une belle couleur gris bleuté foncé.

Ensuite, nous allons placer un widget de cadre dans le formulaire Toplevel. Il contiendra le widget Label que nous utiliserons pour afficher l'image fixe du site de la NASA. Placez-le près du coin supérieur gauche du formulaire principal. Ne vous préoccupez pas trop du placement à ce stade. En utilisant à nouveau l'éditeur d'attributs, assurez-vous que le cadre est sélectionné et définissez la largeur à 714 et la hauteur à 492. Ensuite, ajoutez un widget Label et définissez les positions X et Y dans l'éditeur d'attributs à 2 et 2. Cela créera une petite bordure autour du widget Étiquette lorsqu'il sera réglé à sa taille maximale. Définissez la largeur de l'étiquette à 704, la hauteur à 488 et la couleur de fond à « skyblue3 ». Cela nous donne une belle couleur pour la zone d'affichage visuelle intérieure. Définissez l'alias du widget Label comme « labelImage », et supprimez le texte dans l'attribut text.

Nous allons maintenant terminer le placement de la combinaison Cadre et Étiquette. PAGE dispose d'une fonction qui vous permet de sélectionner plusieurs widgets et de les manipuler en tant que groupe, par exemple en les centrant horizontalement ou verticalement, en les espaçant de manière égale au sein de leur parent, etc. À ce stade, nous voulons simplement centrer horizontalement le cadre dans le formulaire principal. Dans l'arbre des widgets, utilisez le bouton central de la souris pour cliquer sur l'entrée Frame: Frame1.

Notice that it turns green. The black resize handles of the Frame in the main form also turn green to let you know you are in the multi-select mode. We need to select only the Frame, since it is the parent of the image label. Now right-click on the Frame/Image combo in the main form. You will see a context menu appear. Select Center Horizontal from the menu. The Frame will now be centered in the form horizontally. Click Unselect MS in the Widget Tree window. I set the top of my Frame to 10 pixels down from the top of the form (Y position). It’s time to save your project. Name it “NasaStills.tcl”. Now we can start to place the rest of the widgets on our form. We’ll place the first of two Checkbuttons next. Be sure to make it a Tk Checkbutton, not a ttk Themed Checkbutton. I’ll give you the attributes you will want to set in the grid below.

Remarquez qu'il devient vert. Les poignées de redimensionnement noires du cadre dans le formulaire principal deviennent également vertes pour vous indiquer que vous êtes en mode de sélection multiple. Nous devons sélectionner uniquement le cadre, car il est le parent de l'étiquette de l'image. Cliquez maintenant avec le bouton droit de la souris sur la combinaison Cadre/Image dans le formulaire principal. Vous verrez apparaître un menu contextuel.

Sélectionnez Center Horizontal dans le menu. Le cadre sera maintenant centré horizontalement dans le formulaire. Cliquez sur Unselect MS dans la fenêtre Widget Tree. J'ai réglé le haut de mon cadre à 10 pixels en dessous du haut du formulaire (position Y). Il est temps d'enregistrer votre projet. Nommez-le « NasaStills.tcl ».

Nous pouvons maintenant commencer à placer le reste des widgets sur notre formulaire. Nous allons placer le premier des deux boutons de contrôle. Assurez-vous d'en faire un bouton de contrôle Tk, et non un bouton de contrôle ttk Themed.

Dans la grille ci-dessous, je vais vous donner les attributs que vous voudrez définir.

Some information about these attributes. By setting the Active Background (active bg) and select color attributes, we control the colors when the mouse is over the widget (active bg) as well as the color when the status is Checked (select color). Setting the foreground color to antiquewhite2, the text and check area are bright enough to be seen, but not bright enough to cause the check not to show. If we were to set the foreground to full white (#ffffff), you won’t be able to tell when the check is set. Give it a try. Save again and we’ll add three more buttons. Make sure they line up nicely in the row with the Checkbutton. The first one will be the Save button, next is the Reload button, and the last is the Exit button. You might want to sneak a peek at the image above for a reference. Here are the attributes for the Save button. Next the Reload button Finally the Exit button. Remember to make everything line up in the row nicely, and provide a little bit of space between the bottom of the frame and the top of the buttons.

Quelques informations sur ces attributs. En définissant les attributs Active Background (active bg) et select color, nous contrôlons les couleurs lorsque la souris passe sur le widget (active bg) ainsi que la couleur lorsque l'état est Vérifié (select color). En définissant la couleur d'avant-plan sur antiquewhite2, le texte et la zone de vérification sont suffisamment clairs pour être vus, mais pas assez pour que la vérification ne s'affiche pas. Si nous définissons le premier plan en blanc complet (#ffffff), vous ne pourrez pas voir que le contrôle est activé. Faites un essai.

Enregistrez à nouveau et nous allons ajouter trois autres boutons. Veillez à ce qu'ils soient bien alignés avec le bouton de contrôle. Le premier sera le bouton Enregistrer, le suivant sera le bouton Recharger et le dernier sera le bouton Quitter. Vous pouvez jeter un coup d'œil à l'image ci-dessus à titre de référence. Voici les attributs du bouton Enregistrer.

Ensuite, le bouton Recharger

Enfin, le bouton Quitter.

N'oubliez pas de faire en sorte que tout soit bien aligné horizontalement et de laisser un peu d'espace entre le bas du cadre et le haut des boutons.

Save your project again and we’ll finish up with the last three widgets. First we’ll do another Tk Checkbutton. Next will be the Spinbox. You’ll want to make it a bit smaller in width from the default that PAGE gives you. I made mine only 58 pixels wide. How wide you make it is up to you. Here are the attributes. Last but not least, is the Set button. The original author used this to apply the time delay from the Entry widget. I’m going to take advantage of the Spinbox text var to update the delay time every time we get an image. So if you want to include it, that’s fine. If not, that’s not a problem. Save your project and generate your Python modules. Next we’ll work on the code in the _support module. We’ll start with the imports section.

Sauvegardez à nouveau votre projet et nous allons terminer avec les trois derniers widgets. Tout d'abord, nous allons faire un autre bouton de contrôle Tk.

Ensuite, c'est la Spinbox. Vous voudrez lui donner une largeur un peu plus petite que celle qui est proposée par défaut par PAGE. La mienne ne fait que 58 pixels de large. C'est vous qui décidez de sa largeur. Voici les attributs.

Le dernier mais non le moindre, c'est le bouton Set. L'auteur original l'a utilisé pour appliquer le délai du widget Entry. Je vais tirer parti de la variable text de la Spinbox pour mettre à jour le délai chaque fois que nous recevons une image. Si vous souhaitez l'inclure, c'est parfait. Sinon, ce n'est pas un problème.

Sauvegardez votre projet et générez vos modules Python. Ensuite, nous allons travailler sur le code du module _support.

Nous allons commencer par la section des importations.

import sys from os.path import exists from datetime import datetime, timedelta from pathlib import Path import requests from requests.exceptions import Timeout from PIL import Image, ImageTk import shutil I show the import sys line in plain (not bold), since PAGE already has that for us. We’ll also need to import a couple of extra Tkinter modules. from tkinter import Spinbox, messagebox from tkinter import font from tkinter import filedialog from tkinter import constants

import sys

from os.path import exists

from datetime import datetime, timedelta

from pathlib import Path

import requests

from requests.exceptions import Timeout

from PIL import Image, ImageTk

import shutil

Je montre la ligne import sys en maigre (pas en gras), puisque PAGE l'a déjà fait pour nous.

Nous aurons également besoin d'importer quelques modules Tkinter supplémentaires.

from tkinter import Spinbox, messagebox

from tkinter import font

from tkinter import filedialog

from tkinter import constants

If you scroll down the file a little bit, there is a function called set_Tk_var(). We’ll need to make one small change to that function, so I’ll show that one change in bold below. Basically, it sets the default for the spinbox to 45 at startup. def set_Tk_var(): global spinbox spinbox = tk.StringVar() spinbox.set('45') global che47 che47 = tk.IntVar() global che48 che48 = tk.IntVar() The next function in the file should be the init function. PAGE creates this function for us, and again, we need to make a one line addition to it. I’ll put it in bold.

Si vous faites défiler le fichier un peu plus bas, il y a une fonction appelée set_Tk_var(). Nous allons devoir apporter une petite modification à cette fonction, et je vais donc la montrer en gras ci-dessous. En gros, elle fixe la valeur de spinbox à 45 par défaut au démarrage.

def set_Tk_var() :

  global spinbox
  spinbox = tk.StringVar()
  spinbox.set('45')
  global che47
  che47 = tk.IntVar()
  global che48
  che48 = tk.IntVar()

La fonction suivante dans le fichier doit être la fonction init. PAGE crée cette fonction pour nous et, encore une fois, nous devons faire un ajout d'une ligne dans cette fonction. Je vais la mettre en gras.

def init(top, gui, *args, kwargs):

  global w, top_level, root
  w = gui
  top_level = top
  root = top
  startup()

The startup function (right) is run just before the user sees the project main form. Here we set any global variables we need as well as define some constants.

A couple of things you might want to be aware of. We have set the checkboxes to unchecked by using the che48.set(0) and che47.set(0). We also set the spinbox to be disabled at startup. When the user checks the “Auto-reload” checkbox, we’ll set it back to a normal state. We also create (if it doesn’t exist) a very simple text file, which will hold the number of the last saved image file. def init(top, gui, *args, kwargs) :

  global w, top_level, root
  w = gui
  top_level = top
  root = top
  startup()

La fonction startup (à droite) est exécutée juste avant que l'utilisateur ne voie le formulaire principal du projet. Ici, nous définissons toutes les variables globales dont nous avons besoin, ainsi que quelques constantes.

Il y a deux choses que vous devez savoir. Nous avons défini les cases à cocher comme non cochées en utilisant les variables che48.set(0) et che47.set(0). Nous avons également configuré la spinbox pour qu'elle soit désactivée au démarrage. Lorsque l'utilisateur cochera la case « Auto-reload », nous la remettrons dans un état normal. Nous créons également (s'il n'existe pas) un fichier texte très simple, qui contiendra le numéro du dernier fichier image sauvegardé.

def on_chkAspect(): # print('NasaStills_support.on_chkAspect') # sys.stdout.flush() pass We really don’t need to have any code in the chkAspect function, so I commented out the PAGE debugging code and set it to pass. def on_chkTime(): # print('NasaStills_support.on_chkTime') # sys.stdout.flush() if che48.get() == 1: w.Spinbox1.configure(state=tk.NORMAL) else: w.Spinbox1.configure(state=tk.DISABLED) refresh_time = spinbox.get() root.update()

def on_chkAspect() :

  # print('NasaStills_support.on_chkAspect')
  # sys.stdout.flush()
pass

Nous n'avons pas vraiment besoin d'avoir du code dans la fonction chkAspect, donc j'ai commenté le code de débogage PAGE et l'ai mis à pass.

def on_chkTime() :

  # print('NasaStills_support.on_chkTime')
  # sys.stdout.flush()
  if che48.get() == 1:

w.Spinbox1.configure(state=tk.NORMAL)

  else:

w.Spinbox1.configure(state=tk.DISABLED)

  refresh_time = spinbox.get()
  root.update()
  

Here is the chkTime function I told you about. We check if the checkbox variable is equal to 1. If so, we set the spinbox state to normal. Otherwise set it to disabled. def on_btnExit(): # print('NasaStills_support.on_btnExit') # sys.stdout.flush() destroy_window() The on_btnExit is the callback for the Exit button. It simply calls the PAGE created function destroy_window to close the program properly. The on_btnReload function is the callback function that will reload the image. It calls the get_image_from_web function. If you click the Aspect checkbutton, this will force a refresh of the image in whatever aspect ratio is currently set, either 16×9 or 708×480.

Voici la fonction chkTime dont je vous ai parlé. Nous vérifions si la variable checkbox est égale à 1. Si oui, nous mettons l'état de la spinbox à normal. Sinon, nous la désactivons.

def on_btnExit() :

  # print('NasaStills_support.on_btnExit')
  # sys.stdout.flush()
  destroy_window()

Le on_btnExit est le callback pour le bouton Exit. Il appelle simplement la fonction destroy_window créée par PAGE pour fermer le programme correctement.

La fonction on_btnReload est la fonction de rappel qui va recharger l'image. Elle appelle la fonction get_image_from_web. Si vous cliquez sur le bouton Aspect, cela forcera un rafraîchissement de l'image dans le rapport d'aspect actuellement défini, soit 16:9, soit 708×480.

def on_btnReload(): # print('NasaStills_support.on_btnReload') # sys.stdout.flush() global feed_url get_image_from_web(feed_url) The on_btnSave callback function (top right) will attempt to save the current image to a file. It first opens the filecounter.txt file (which is created at startup if it doesn’t exist), reads the number of the last file (0 if none have been saved), increments by one, and then appends that value to “NasaStills” in the filename “NasaStills?.png”. All files are saved directly to the source code folder. The on_btnSet callback function is not needed if you decided not to include the Set button. If you did, and followed the instructions, this will cause an information dialog box to be displayed by calling the showinfo function.

def on_btnReload() :

  # print('NasaStills_support.on_btnReload')
  # sys.stdout.flush()
  global feed_url
  get_image_from_web(feed_url)

La fonction de rappel on_btnSave (en haut à droite) va tenter d'enregistrer l'image actuelle dans un fichier. Elle ouvre d'abord le fichier filecounter.txt (qui est créé au démarrage s'il n'existe pas), lit le numéro du dernier fichier (0 si aucun n'a été enregistré), l'incrémente d'une unité, puis ajoute cette valeur à « NasaStills » dans le nom de fichier « NasaStills?.png ». Tous les fichiers sont enregistrés directement dans le dossier du code source.

La fonction de rappel on_btnSet n'est pas nécessaire si vous avez décidé de ne pas inclure le bouton Set. Si vous l'avez fait, et que vous avez suivi les instructions, cela provoquera l'affichage d'une boîte de dialogue d'information en appelant la fonction showinfo.

def on_btnSet(): # print('NasaStills_support.on_btnSet') # sys.stdout.flush() showinfo(“Set”, “Set function is not yet implemented”) The on_spinChange callback function fires every time the spinbox is incremented or decremented. It simply sets the global variable refresh_time. It’s an artifact from an earlier version that I did, since the on_tick timer function currently gets the value directly from the spinbox to set the next timer value. I provided it here just as an example of how to get the value of the spinbox. def on_spinChange(): # print('NasaStills_support.on_spinChange') # sys.stdout.flush() global refresh_time refresh_time = spinbox.get()

def on_btnSet() :

  # print('NasaStills_support.on_btnSet')
  # sys.stdout.flush()
  showinfo("Set", "La fonction Set n'est pas encore implémentée")

La fonction de rappel on_spinChange se déclenche chaque fois que la spinbox est incrémentée ou décrémentée. Elle définit simplement la variable globale refresh_time. C'est un artefact d'une version antérieure que j'ai faite, puisque la fonction de minuterie on_tick obtient actuellement la valeur directement de la spinbox pour définir la prochaine valeur de la minuterie. Je l'ai fourni ici juste comme un exemple de comment obtenir la valeur de la spinbox.

def on_spinChange() :

  # print('NasaStills_support.on_spinChange')
  # sys.stdout.flush()
  global refresh_time
  refresh_time = spinbox.get()
  

The get_image_from_web function (next page, top right) is where the first part of the “magic” happens. We use the requests.get method from the requests library to grab an image from a website. The image is received and saved as a .png image. Then, we verify the width and height of the image. If it is the correct size (704×480), and the aspect ratio checkbutton is not checked, then it is simply saved as a local image. If the aspect ratio checkbutton IS checked, then the PIL library resizes to the 16×9 format before it gets saved. Finally, we set the image attribute of the Label to the image we just downloaded. We also save it to a convenient local file, in case the user wants to save it. The on_tick function (page after next, top right) is where the rest of the “magic” happens, in my mind at least. Here we use the root.after function of Tkinter to create a timer event. The first thing we do is to get the current value of the spinbox (between 45 and 300) and store it in a temporary variable rt (standing for refresh time). It then calls the get_image_from_web function to get the image and refresh the Label. Finally the callback is enabled with the time in milliseconds (rt * 1000) and reset the callback for the next call.

La fonction get_image_from_web (page suivante, en haut à droite) est l'endroit où la première partie de la « magie » opère. Nous utilisons la méthode requests.get de la bibliothèque requests pour récupérer une image sur un site Web. L'image est reçue et enregistrée au format .png. Ensuite, nous vérifions la largeur et la hauteur de l'image. Si la taille est correcte (704 x 480) et que le bouton de vérification du rapport hauteur/largeur n'est pas coché, l'image est simplement enregistrée comme une image locale. Si le bouton de vérification du rapport hauteur/largeur EST coché, la bibliothèque PIL redimensionne l'image au format 16:9 avant de l'enregistrer. Enfin, nous définissons l'attribut image de l'étiquette avec le nom de l'image que nous venons de télécharger. Nous l'enregistrons également dans un fichier local pratique, au cas où l'utilisateur voudrait l'enregistrer.

La fonction on_tick (2 pages plus loin, en haut à droite) est l'endroit où le reste de la « magie » fait effet, du moins dans mon esprit. Ici, nous utilisons la fonction root.after de Tkinter pour créer un événement de timer. La première chose que nous faisons est de récupérer la valeur actuelle de la spinbox (entre 45 et 300) et de la stocker dans une variable temporaire rt (pour refresh time). On appelle ensuite la fonction get_image_from_web pour obtenir l'image et rafraîchir l'étiquette. Enfin, le callback est activé avec le temps en millisecondes (rt * 1000) et réinitialise la callback pour le prochain appel.

The showinfo function takes two parameters, title and message, then calls the Tkinter message box to show the message box to the user. We also provide the parent (which in this case will always be root, but that will keep the messagebox over the actual application and set the icon to the INFO icon. def showinfo(titl, msg): messagebox.showinfo(titl, msg, parent=root, icon=messagebox.INFO) The showerror function is almost exactly the same as the showinfo function, but shows an error message box with an error icon instead. def showerror(titl, msg): messagebox.showerror(titl, msg, parent=root, icon=messagebox.ERROR) Finally, we have the centre_screen function, which takes the width and height of our main form (which is provided in the GUI python file that PAGE creates). It then uses the screen width and height to calculate the centre of the screen and form.

La fonction showinfo prend deux paramètres, le titre et le message, puis appelle la boîte de message Tkinter pour montrer la boîte de message à l'utilisateur. Nous fournissons également le parent (qui dans ce cas sera toujours root, mais qui gardera la boîte de message au dessus de l'application actuelle) et définit l'icône comme étant l'icône INFO.

def showinfo(titl, msg) :

  messagebox.showinfo(titl, msg, parent=root, icon=messagebox.INFO)

La fonction showerror est presque exactement la même que la fonction showinfo, mais affiche une boîte de message d'erreur avec une icône d'erreur à la place.

def showerror(titl, msg) :

  messagebox.showerror(titl, msg, parent=root, icon=messagebox.ERROR)

Enfin, nous avons la fonction centre_écran, qui prend la largeur et la hauteur de notre formulaire principal (qui est fourni dans le fichier python GUI que PAGE crée). Elle utilise ensuite la largeur et la hauteur de l'écran pour calculer le centre de l'écran et du formulaire.

def centre_screen(wid, hei): ws = root.winfo_screenwidth() hs = root.winfo_screenheight() x = (ws / 2) - (wid / 2) y = (hs / 2) - (hei / 2) root.geometry('%dx%d+%d+%d' % (wid, hei, x, y)) That’s it. Our project is completed. I feel happy about the functionality that Tkinter provides against the PySimpleGUI toolkit. When I did the first version of the program, it took me only about 30 minutes to design the PAGE GUI form and write or borrow the code snippets from some of my previous programs. The length of the actual support module with comments (LOTS of comments), and double spacing between functions, is only about 230 lines of code, which isn’t too bad. You can find the code already created for you and ready to run at my github repository at https://github.com/gregwa1953/FCM-175. Until next time, as always; stay safe, healthy, positive and creative!

def centre_screen(wid, hei) :

  ws = root.winfo_screenwidth()
  hs = root.winfo_screenheight()
  x = (ws / 2) - (wid / 2)
  y = (hs / 2) - (hei / 2)
  root.geometry('%dx%d+%d+%d' % (wid, hei, x, y))

Voilà, c'est fait. Notre projet est terminé. Je suis content que Tkinter soit si fonctionel par rapport à la boîte à outils PySimpleGUI. Quand j'ai fait la première version du programme, il ne m'a fallu qu'environ 30 minutes pour concevoir le formulaire PAGE GUI et écrire ou emprunter les bouts de code de certains de mes programmes précédents. La longueur du module de support actuel, avec des commentaires (BEAUCOUP de commentaires) et un double espacement entre les fonctions, ne fait qu'environ 230 lignes de code, ce qui n'est pas si mal.

Vous pouvez trouver le code déjà créé pour vous et prêt à être exécuté sur mon dépôt github à l'adresse https://github.com/gregwa1953/FCM-175.

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

issue175/python.txt · Dernière modification : 2021/12/03 14:47 de andre_domenech