#Python : classes et méthodes (partie 2) – mise en pratique

Pour une lecture plus agréable (page plus large), je vous invite à cliquer sur ce lien et à lire ce chapitre dans la rubrique consacrée au langage Python.

Nous allons confier à deux programmeurs en herbe, Briface et Jobriel, le soin de rédiger le code d’une petite application à caractère pédagogique. Elle consiste à faire apparaître des formes générées aléatoirement.

Jobriel va rédiger le code nécessaire à la création de l’environnement graphique, c’est-à-dire les widgets tandis que Briface, de son côté, va rédiger le module permettant de faire apparaître de manière aléatoire, des formes qui sont soit des triangles, soit des cercles, soit des carrés. La couleur de ces formes est également définie de manière aléatoire.

Au final, voici ce que nos deux gugusses doivent produire :

Le code de Jobriel

Commençons par le code de notre ami Jobriel :


#!/usr/bin/env python3
# -*- coding: utf8 -*-

from tkinter import*
import choice

class MainProg :
    """Création de l'environnement graphique de l'application"""

    def __init__(self, main_window, h = 300, w = 300, color = 'white'):
        """Méthode constructeur"""

        self.main_window = main_window
        self.h = h
        self.w = w
        self.color = color

    def widgets(self):
        """Création des widgets"""

        self.main_frame = Frame(self.main_window, height=self.h,\
                      width=self.w, bg=self.color)
        self.main_frame.pack()

        self.canevas = Canvas(self.main_frame, height=400, width=self.w,\
                   bg=self.color)
        self.canevas.pack(side=TOP)

        self.frame_1 = Frame(self.main_frame, height=100, width=self.w,\
                   bg=self.color)
        self.frame_1.pack(side=BOTTOM)

        self.random_choice = choice.RandomObjects(self.canevas)

        self.button = Button(self.frame_1, height=1, width=5, text='Afficher',\
                  relief='ridge', bd=3, bg='navy', fg='white',\
                  font=('Times', 14, 'bold'), padx=20,\
                  command = self.random_choice.random_choice)
        self.button.pack(padx=20, pady=10)

#===== MAIN ===================================================================

if __name__ == '__main__':

    main_window = Tk()
    main_window.title('Une forme au hasard')

    main_page = MainProg(main_window, 500, 500)
    main_page.widgets()

    main_window.mainloop()

ligne n° 4 : importation du module tkinter.
ligne n° 5 : importation du module choice qui va nous permettre de faire apparaître de manière aléatoire, différentes formes telles que des carrés, des ronds, des triangles. Les couleurs de ces formes sont également définies de manière aléatoire. Pour les deux formes d’importation, je vous renvoie au chapitre sur les modules. Personnellement, je préfère la forme d’importation ci-dessous car il n’y a pas de risques de conflits entre des variables portant le même nom. En outre, elle permet de raccourcir le nom du module. tkinter devient tk :

import tkinter as tk 

Pour de plus amples renseignements sur la bibliothèque tkinter, je vous invite à consulter cette documentation en français.

Ligne n° 43 : Nous trouvons une condition introduisant du code qui va s’exécuter uniquement si le programme est lancé de manière indépendante et non pas comme un module. Ici, c’est le cas. Par conséquent, cette partie du code s’exécute.

Ligne n° 45 : Création de l’objet main_window (fenêtre principale) par instanciation de la classe Tk() issue du module tkinter. Si j’avais écrit ceci à la ligne n° 4 : import tkinter as tk, alors la ligne n° 45 aurait été : main_window = tk.Tk().

Ligne n° 46 : application de la méthode title() sur l’objet main_window. Elle donne un titre à la fenêtre.

À ce stade, si on exécute le programme, voici ce qu’on obtient :

main_window

Ligne n° 48 : Création de l’objet main_page par instanciation de la classe MainProg(). Nous lui passons trois arguments qui sont main_window (la fenêtre mère), 500 (la hauteur de l’application) et 500 (la largeur). Nous choisissons de ne pas lui passer de paramètre de couleur. Ce sera donc la couleur par défaut qui va s’appliquer.
Ligne n° 49 : Application de la méthode widgets() sur l’objet main_page.

Ligne n° 51 : Application de la méthode mainloop() sur l’objet main_window ce qui a pour effet de déclencher le réceptionnaire d’événements et de permettre l’exécution du programme.

Ligne n° 7 : On remonte tout en haut! Définition d’une classe fondamentale baptisée MainProg
Ligne n° 10 : Méthode constructeur qui prend quatre paramètres:

  • self qui correspond à la référence de l’instance, c’est-à-dire l’objet main_page que nous avons instancié à la ligne n° 48. je rappelle qu’une méthode d’instance prend toujours au moins cet argument et que par convention, il est nommé self.
  • main_window qui correspond à la fenêtre principale et dont nous avons besoin pour instancier les widgets enfants.
  • h, w et color sont des paramètres qui possèdent une valeur par défaut. Si nous décidons de les ignorer, ce seront donc ces valeurs qui vont s’appliquer. C’est ce que nous avons fait avec color. Nous avons ignoré ce paramètre. Pour ce qui est des autres valeurs par défaut (h=300, w=300), nous avons choisi de les remplacer par 500 (voir ligne n° 48). Si nous décidons de ne pas respecter l’ordre de déclaration, il faut alors préciser le paramètre. Par exemple :

main_page = MainProg(main_window, color = ‘blue’, w=260, h=600)

Ligne n° 11 : Docstring

Lignes n° 13 à 16 : Création des attributs d’instance
Ligne n° 18 : Méthode widgets (création de tous les widgets de l’application)
Lignes n° 21 et 22 : Création de l’objet self.main_frame par instanciation de la classe Frame. Cette dernière créé un cadre que l’on peut remplir avec d’autres widgets. Nous pouvons lui passer plusieurs arguments. Notez bien la largeur (width) et la hauteur (height). Normalement, vous devez être capables de retrouver leurs valeurs. Notez également la barre oblique (antislash) qui permet d’écrire une instruction sur plusieurs lignes. La fondation Python préconise de ne pas dépasser 79 caractères par ligne, ce qui est assez strict. Dans la mesure du possible, il faut respecter cette convention.

Ligne n° 23 : Application de la méthode pack(). Cette dernière place le widget. Elle peut prendre des paramètres tels que TOP, BOTTOM, LEFT ou RIGHT qui poussent le widget dans la direction donnée.

À ce stade, si nous exécutons le programme, voici ce que nous obtenons. J’ai volontairement et provisoirement coloré en rouge, le fond du widget :

main_frame

Lignes n° 25 et 26 : Création de l’objet self.canevas par instanciation de la classe Canvas. C’est ce widget qui va nous permettre de placer les formes aléatoires grâce à différentes méthodes qui lui sont liées.
Ligne n° 27 : Méthode de placement pack(). Cette fois-ci, elle prend un paramètre (side=TOP) pour pousser le widget vers le haut.
Lignes n° 29 à 31: Nous créons un nouvel objet de type Frame que nous plaçons tout en bas de sa fenêtre parente (side=BOTTOM). Dans ce widget, nous placerons le bouton Afficher.

Ligne n° 33: Création de l’objet random_choice par instanciation de la classe RandomObjects du module importé choice. Nous lui passons un paramètre qui est self.canevas. Donc, cela nous donne:

self.random_choice = choice.RandomObjects(self.canevas)

Je rappelle que l’utilisation du point en Python signifie l’appartenance. Ainsi, la classe RandomObjects appartient au module choice.

Lignes n° 35 à 38: Création du bouton afficher par instanciation de la classe Button. Celle-ci prend plusieurs paramètres qui sont :

  • La hauteur
  • La largeur
  • Le texte à afficher
  • Le relief de la bordure
  • L’épaisseur de la bordure
  • La couleur de fond
  • La couleur du texte
  • La police et la fonte de caractères
  • L’espacement horizontal du texte à l’intérieur du widget
  • La commande que le bouton déclenche.

Pour ce dernier paramètre, nous appliquons la méthode random_choice issue du module importé choice, sur l’objet que nous avons créé (random_choice). Cela nous donne :

command=self.random_choice.random_choice

Alors là, vous allez me dire : « Mais Ordinosor! Tu bé, tu bé-bé, tu bégayes? »

Absolument pas! Il n’y a aucun risque de collision ou de confusion puisque ces deux noms identiques n’évoluent pas dans les mêmes espaces.

Notez bien que la commande déclenche une méthode lorsque nous cliquons sur le bouton. Il ne faut donc pas mettre les parenthèses () à la fin de la commande car cela aurait pour effet de shunter le bouton et d’activer la méthode dès le lancement du programme! Donc, il ne faut pas écrire :

command=self.random_choice.random_choice()

Ligne n° 39: La méthode pack() place le bouton dans sa fenêtre parente en réservant un espace vertical (pady=20) et horizontal (padx=20).

J’ai coloré provisoirement les différents widgets pour mieux les visualiser. Si nous exécutons le programme à ce stade, voici ce que nous obtenons:

  • En rouge, le widget self.main_frame
  • En blanc, le widget self.canevas
  • En jaune, le widget self.frame_1
  • En bleu, le widget self.button

button2

Merci pour ce travail, Jobriel! À présent, remettons tous les fonds des widgets en blanc Cliquons sur le bouton et voyons ce que notre ami Briface a codé…

Le code de Briface


#!/usr/bin/env python3
# -*- coding: utf8 -*-

from tkinter import*
import random

class RandomObjects :
    """Instancie aléatoirement les formes (triangles, cercles, carrés).
    Les couleurs de ces formes sont également définies de manière aléatoire"""

    def __init__(self, canevas):
        """Constructeur"""

        self.canevas = canevas

    def square(self):
        """Création du carré"""

        self.square_object = self.canevas.create_rectangle(200, 150, 300, 250,\
                             fill=self.random_color)

    def circle(self):
        """Création du cercle"""

        self.circle_object = self.canevas.create_oval(200, 150, 300, 250,\
                             fill=self.random_color)

    def triangle(self):
        """Création du triangle"""

        self.polygon = self.canevas.create_polygon(250, 150, 200, 250, 300,\
                       250,fill=self.random_color)

    def random_choice(self):
        """cf docstring class RandomObjects"""

        self.canevas.delete('all')
        self.random_color=random.choice(['green','red','yellow','blue','black'])
        random.choice([self.square, self.circle, self.triangle])()

Ligne n° 5 : importation du module random qui va nous permettre de faire des choix aléatoires.
Ligne n° 7: Création la classe RandomObjects.
Ligne n° 11: Méthode constructeur avec deux paramètres, la référence d’instance self et canevas.
Ligne n° 13: Déclaration d’une variable d’instance.
Ligne n° 16: Définition d’une méthode qui dessine un carré.
Ligne n° 19: Pour cela, nous utilisons la méthode create_rectangle qui prend quatre coordonnés en arguments ainsi qu’une couleur de remplissage.
Ligne n° 22: Définition d’une méthode qui dessine un cercle plein.
Ligne n° 25: Pour cela, nous utilisons la méthode create_oval qui prend quatre coordonnés en arguments ainsi qu’une couleur de remplissage.
Ligne n° 28: Définition d’une méthode qui dessine un triangle.
Ligne n° 31: Pour cela, nous utilisons la méthode create_polygon qui prend six coordonnés en arguments ainsi qu’une couleur de remplissage.

Pour de plus amples détails, je vous invite à consulter cette documentation en français.

Ligne n° 34: Définition de la méthode random_choice.
Ligne n° 37: Nous appliquons la méthode delete(‘all’) sur le widget self.canevas pour effacer une éventuelle forme avant que la suivante ne prenne sa place.
Ligne n° 38: Déclaration de la variable self.random_color. La valeur qui lui est affectée est une couleur choisie au hasard dans la liste passée en argument à la méthode random.choice.

Là encore, attention de ne pas confondre la méthode random_choice que nous avons nous-même définie et la méthode random.choice, c’est-à-dire la méthode choice du module importé random (ligne n° 5)!

Ligne n° 39: Cette fois-ci, random.choice choisit au hasard la méthode qui va matérialiser la forme. Pour que cette méthode exécute ses instructions, nous rajoutons tout à la fin, deux parenthèses. Sans ces deux parenthèses, rien ne se passerait:
random.choice([self.square, self.circle, self.triangle])()

Merci à toi , Briface, pour ce module qui exécute à la perfection la tâche qui lui a été impartie.

Ce diaporama nécessite JavaScript.

#Python : programmer une photothèque

Bonjour à tous,

Aujourd’hui, je vous propose une courte vidéo Youtube qui me permet de vous montrer à quoi ressemble mon programme Ma photothèque écrit en langage Python (Ai-je besoin de le préciser…).

Il permet de créer des albums photos. Voici le code :

//pastebin.com/embed_iframe/cJiyHY80

#Python : Créer une photothèque

Edit du 7 Novembre 2016

Voici le code du programme :

//pastebin.com/embed_iframe/cJiyHY80

Bonsoir,

Aujourd’hui, scoop! Il faisait un temps gris et pluvieux dans le Nord de l’Allemagne. Alors, plutôt que de m’adonner à une activité extérieure, je me suis courageusement replié sur mon siège de bureau. Et pour me changer les idées, je me suis dit:

Continuer à lire … « #Python : Créer une photothèque »

#Python : Ordinotes, mon logiciel « post-it »

Dans la logithèque, la bibliothèque de logiciels proposée par Ubuntu, les mini-programmes pour enregistrer de simples notes sont légion. Entre Xpad, Knotes, GNote et compagnie, j’aurais du trouver mon bonheur mais ce n’est pas le cas. Aucun ne répond à mes exigences qui ne sont pourtant pas très élevées.  Compte tenu du fait qu’on ne se lance pas dans la rédaction d’un  roman sur des post-its de 60 millimètres par 60, il me semble évident que ce type de logiciel doit être simple, rapide et doté de fonctionnalités minimales. Un post-it, ça sert à écrire des notes « à la volée ».

Je me suis efforcé de respecter ce cahier des charges et c’est ainsi qu’est né Ordinotes, mon logiciel « post-it » personnel.

Code : //pastebin.com/embed_iframe/9mE0uS6J

ordinotes

Première chose, il n’est pas carré mais rectangulaire, ce qui permet de rédiger de vraies phrases sans être obligé de faire un retour chariot après trois mots. L’en-tête se résume à un bouton Menu (☰), à la date (automatiquement mise à jour grâce à l’importation du module localtime) et à un champ d’entrée pour renseigner l’objet. Aussi longtemps que celui-ci est vide, il n’est pas possible de rédiger la note car l’éditeur de texte est verrouillé. J’utilise pour ce faire la méthode isspace(). Dès que l’objet est renseigné, l’éditeur obtient le focus. Il est possible de modifier la date car il s’agit non pas d’un widget Label mais d’un champ d’entrée.

Cliquons sur l’icône menu (☰) et découvrons ensemble ses fonctionnalités. Pour votre gouverne, sachez que ce genre d’icône s’appelle une icône hamburger.

ordinotes_2

Le menu est noir car je trouve tout simplement que le noir et le jaune sont deux couleurs qui se marient bien ensemble. Il est tout à fait possible de modifier le programme pour les remplacer par d’autres couleurs. Il est même possible de le proposer directement à l’utilisateur grâce au sous-module colorchooser. Ce n’est pas bien compliqué.

Cliquons sur « Ouvrir ». L’on voit clairement que j’ai déjà rédigé quatre notes. Il n’est donc pas étonnant de voir apparaître quatre boutons. Il vous suffit de cliquer sur le bouton de votre choix pour que la note se matérialise :

ordinotes_3

Vous pouvez bien évidemment ouvrir toutes les notes et visualiser ainsi toutes les tâches qu’il vous reste à accomplir. C’est un peu le principe des post-it. J’ai défini une variable de classe qui fait apparaître les notes en cascade, avec un décalage de 100 pixels en hauteur et en largeur.

Le code est ici :

//pastebin.com/embed_iframe/9mE0uS6J

Il me reste encore quelques fenêtres d’avertissement à rajouter, notamment lorsque l’utilisateur décide de supprimer tout ou partie de ses notes. C’est l’affaire d’un petit quart d’heure.

Ai-je besoin de préciser que vous êtes libres de recopier ce code, de l’utiliser et de le modifier comme bon vous semble?

#Python : rajouter un champ de recherche dans mon agenda-calendrier

Bonjour à tous,

Aujourd’hui, dans mon agenda-calendrier qui me comble de joie, j’ai instancié un champ de recherche couplé à un bouton représentant une loupe. Il m’a fallu plusieurs jours pour arriver à mes fins mais je dois reconnaître que je suis bien content car il s’agit d’une fonction fort utile au demeurant. Le code est ici:

//pastebin.com/embed_iframe/C0eZJk5u

Comme vous pouvez le voir dans le diaporama ci-dessous, j’ai entré le mot « Espéranto » dans le champ de recherche. Vous pouvez mettre des majuscules ou des minuscules si cela vous chante… Peu importe puisque de toute manière, grâce à la méthode lower() que j’utilise dans mon code, toutes les lettres sont transformées en minuscules.

Lorsque je lance la recherche, la fonction (ou plutôt la méthode) ouvre une fenêtre Toplevel et me retourne toutes les dates où apparaît l’occurrence recherchée. Elle me les retourne sous forme de boutons, c’est-à dire que si vous cliquez sur le bouton 17/09/2016 par exemple, cela provoque l’ouverture de la page correspondante. Regardez comme c’est beau :

Ce diaporama nécessite JavaScript.

Oui, mais keskispasse si par hasard l’occurrence n’est présente dans aucune des pages de l’agenda? Eh bien là encore, pas de panique! La méthode vous prévient en ouvrant une fenêtre Toplevel contenant un message idoine… Le fond est rouge pour bien vous faire comprendre que votre recherche n’a rien donné :

recherche4

#Python : créer une horloge à aiguilles (suite et fin)

Bonjour,

Il y a quelques jours, je vous avais dit que je m’étais lancé dans la programmation d’une horloge et que j’éprouvais des difficultés à amorcer son mécanisme.

Eh bien aujourd’hui, je suis heureux de vous annoncer que j’ai réussi à surmonter cet obstacle. Désormais, comme vous pouvez le constater dans la vidéo ci-dessous prise vers onze heures moins dix, les aiguilles se meuvent dans la direction du futur.

Voici le code:

//pastebin.com/embed_iframe/7ad91Vg8

Il me faudra encore un peu de temps pour expliquer en détails comment fonctionne la méthode move, notamment l’utilité des fonctions cosinus et sinus. Je me suis inspiré d’un bout de code que j’ai trouvé sur internet. En clair, j’ai pompé en partie les lignes 62 à 64 et depuis quelques jours, je me suis plongé dans un cours de trigonométrie. Je manipule les sinus, les cosinus, les hypoténuses etc… dans l’espoir de pouvoir vous fournir une explication détaillée de ces trois lignes obscures.

Pour ce qui est du déplacement proprement dit, j’utilise la récursivité (ligne 65) : La méthode move s’appelle elle-même toutes les mille millièmes de seconde, c’est-à-dire toutes les secondes (ligne 66).

#Python : créer une horloge à aiguilles

Bonjour,

Je me suis lançé dans un nouveau projet que je croyais tout à fait abordable mais qui s’avère être beaucoup plus difficile que prévu. Je voulais programmer une horloge. La dessiner a été un jeu d’enfant. Le problème, c’est de la faire fonctionner. Il faut bien que les aiguilles tournent! Et pour les faire tourner, il faut manipuler des outils trigonométriques tels que le sinus et le cosinus, deux olibrius dont j’ai gardé un très vague souvenir de mes années lycée.

Donc, tout ça pour vous dire que mon horloge, elle est très bien… sauf qu’elle marche pas! Il va falloir que je me plonge dans un cours de trigonométrie pour les brêles avant d’espérer voir les aiguilles se mouvoir. C’est pas gagné.

Pour ce qui est de dessiner l’horloge, voici comment j’ai procédé. J’ai d’abord dessiner un cercle avec la méthode Canvas.create_oval:

horloge

Ensuite, j’ai dessiné quatre cercles rouges de même rayon que le cercle vert et dont le centre correspond à la jonction entre le périmètre du cercle vert et les deux lignes perpendiculaire et parallèle. Puis, j’ai tiré des lignes bleues partant du centre du cercle vert jusqu’aux jonctions entre le cercle vert et les cercles rouges. Pour cela, j’utilise la méthode Canvas.create_line:

horloge2

Par ce procédé, j’obtiens l’emplacement exact des chiffres de l’horloge que je m’empresse de dessiner grâce à la méthode Canvas.create_text:

horloge3

Je dessine les aiguilles puis j’efface les lignes bleues et les cercles rouges. Mon horloge se matérialise dans toute sa beauté minimale. Il ne me reste plus qu’à trouver le moyen de la faire fonctionner.

La suite est ici… Désormais, l’horloge fonctionne.