Interface Graphique : Gestion du temps

Les interfaces graphiques servent à rendre les programmes plus conviviaux. Elles sont pratiques à utiliser mais elles demandent un peu de temps pour les concevoir.
Un programme fonctionne de manière différente. Il n’exécute plus successivement les instructions mais attend un événement - pression d’une touche du clavier, clic de souris - pour exécuter une fonction. C’est comme si le programme avait une multitude de points d’entrée.

Il existe plusieurs modules permettant d’exploiter les interfaces graphiques. Le plus simple est le module tkinter présent lors de l’installation du langage Python. Ce module est simple mais limité. Le module wxPython est plus complet mais un peu plus compliqué dans son utilisation. Toutefois, le fonctionnement des interfaces graphiques sous un module ou un autre est identique. C’est pourquoi ce chapitre n’en présentera qu’un seul, le module tkinter. Pour d’autres modules, les noms de classes changent mais la logique reste la même : il s’agit d’associer des événements à des parties du programme Python.

Les interfaces graphiques évoluent sans doute plus vite que les autres modules, des composantes de plus en plus complexes apparaissent régulièrement. Un module comme wxPython change de version plusieurs fois par an. Il est possible de trouver sur Internet des liens qui donnent des exemples de programme. Une excellente source de documentation sont les forums de discussion qui sont un lieu où des programmeurs échangent questions et réponses. Un message d’erreur entré sur un moteur de recherche Internet permet souvent de tomber sur des échanges de ce type, sur des problèmes résolus par d’autres.

Introduction

Un programme muni d’une interface graphique fonctionne différemment d’un programme classique. Un programme classique est une succession presque linéaire d’instructions.

Il y a un début ou point d’entrée du programme et aucun événement extérieur ne vient troubler son déroulement Avec une interface graphique, le point d’entrée du programme est masqué : il est pris en compte automatiquement. Du point de vue du programmeur, le programme a plusieurs points d’entrée : une simple fenêtre avec deux boutons propose deux façons de commencer et il faut prévoir une action associée à chaque bouton. La conception d’une interface graphique se déroule généralement selon deux étapes. La première consiste à dessiner l’interface, c’est-à-dire choisir une position pour les objets de la fenêtre (bouton, zone de saisie, liste déroulante, ...). La seconde étape définit le fonctionnement de la fenêtre, c’est-à-dire associe à chaque objet des fonctions qui seront exécutées si un tel événement se réalise (pression d’un bouton, pression d’une touche, ...).

Pour le moment, nous allons supposer que ces deux étapes sont scindées même si elles sont parfois entremêlées lorsqu’un événement implique la modification de l’apparence de la fenêtre. Nous allons décrire des objets que propose le module tkinter. Ensuite on présente la manière de les disposer dans une fenêtre et on décrit les événements et le moyen de les relier à des fonctions du programme. Enfin nous allons voir comment gérer des images c’est-à-dire comment dessiner sur la fenêtre, placer des images et les déplacer.

Le temps est une notion importante dans la réalisation d’un jeu : apparition d’un bonus durant un temps limité, connaissance de la durée de jeu et même gestion de tout ce qui est animation d’un personnage … Dans ce chapitre, nous allons donc étudier comment accéder à l’horloge de l’ordinateur et comment déclencher une action à intervalle régulier.

Gestion de l'horloge

Python dispose d’un module time que nous devrons importer pour connaître l’heure actuelle. Nous allons représenter trois formats pour représenter le temps en Python. Selon les besoins nous utiliserons l’un d’entre eux.

L'heure POSIX

L’heure POSIX est une mesure du temps qui représente le nombre de secondes écoulées depuis le 1er janvier 1970 à 00 : 00 : 00 UTC jusqu’à l’événement à dater. Cette date, représentant l’origine de l’heure POSIX appelée l’epoch POSIX, dépend souvent du système d’exploitation et parfois du langage. En réalité cette date importe peu car, pour nous, il s’agira souvent de calculer la différence entre l’heure POSIX du début de partie et l’heure POSIX actuelle pour connaître le nombre de secondes écoulées depuis le début de la partie.

FonctionEffet
time.time( )Renvoie l’heure POSIX actuelle (en secondes donc).
time.sleep(t)Effectue une pose de t secondes.

Exemple 17


# -*-coding:Latin-1 -*
import os # on import le module os qui dispose de variables et de fonctions
import time
debut, actu = time.time(), time.time()
while actu - debut < 6 :
    print ("Le jeu est commencé depuis",int(actu - debut),"secondes.")
    time.sleep(2)
    actu = time.time()
print ("C'est fini !")

1. Que fait ce programme ?

2. Modifie ce programme pour qu’il soit de compte à rebours qui commence à 5 secondes et s’arrête quand le temps est écoulé.

La structure du temps

Une structure temps en Python est donnée sous forme de ce que l’on appelle un named tupple d’entiers que l’on pourrait traduire s’il le fallait en " t-upplet nommé ". Il s’agit encore d’un nouveau type de données pour lequel nous n’entrerons pas dans les détails. Pour simplifier, disons qu’il s’agit d’un ensemble de valeurs indexées par un nom. Pour la structure temps, voici la liste des indices et noms accessibles.

indicenomvaleur
0tm_yearAnnée
1tm_monMois(1 à 12)
2tm_dayJour(1 à 31)
3tm_hourHeures(0 à 23)
4tm_minMinutes
4tm_secSecondes
4tm_wdayNuméro du jour dans la semaine (0=lundi à 6=dimanche).
4tm_ydayNuméro du jour dans l’année (0 à 366).
4tm_isdstIndique le changement d’heure locale(-1, 0 ou 1).

Si st est une structure temps, on peut alors récupérer chaque valeur de deux manières (exemple pour l’année) :

  • En utilisant l’indice de la valeur désirée : st[0]
  • En utilisant le nom de la valeur : st.tm_year

Ces valeurs sont en lecture seule et ne peuvent donc pas être modifiées.

Voici deux fonctions qui permettent de passer de l’heure POSIX à une structure temps et inversement :

FonctionEffet
time.localtime(t)Convertit une heure POSIX t en structure temps. Si t n’est pas précisé, renvoie l’heure locale actuelle.
time.mktime(st)Convertit une structure temps st en heure POSIX.

Format d’affichage d’une date

Pour faciliter l’affichage de messages clairs côté utilisateur, voici deux fonctions qui permettent de passer d’une structure temps à une chaîne de caractères et inversement :

FonctionEffet
time.strftime(fmt, tps)Renvoie une chaîne au format fmt représentant la date tps renseignée en structure temps. Si tps n’est pas précisée, l’affichage est celui de l’heure locale actuelle.
time.strptime(txt, fmt)Transforme une chaîne de caractères txt au format fmt en structure temps.

Le format d’affichage d’une date f mt évoqué est ce que l’on appelle une chaine de caractères formatée contenant des clés. En voici quelques exemples :

cléContenu
%dJour du mois (entre 1 et 31).
%Hau format 24 heures.
%IL’heure au format 12 heures.
%jNuméro du jour dans l’année (1 à 366).
%mMois (1 à 12).
%MMinutes.
%SSecondes.
%UNuméro de la semaine.
%wNuméro du jour de la semaine (0 = lundi).
%yDeux derniers chiffres de l’année.
%YAnnée (à 4 chiffres).

Exemple 18


# -*-coding:Latin-1 -*
import time
f = "Nous sommes le %d/%m/%y, il est %H:%M:%S"
for s in range(10):
    print(time.strftime(f))
    time.sleep(1)

Que fait ce programme ?

Résumé

Voici comment passer d’une structure à l’autre

exemple18

Répéter à intervalle régulier

Jusqu’à présent lorsque nous voulions animer quelque chose à l’écran, nous utilisant des boucles, des pauses et des rafraichissements forcés d’écran. Cette méthode n’est clairement pas utilisable dans un jeu car d’une part, si on utilise une boucle pour animer un personnage, tout le reste des animations se trouve interrompu ; d’autre part, vous l’aurez peut être remarqué, durant ce type d’animation toute la fenêtre devient inactive, il est impossible de cliquer sur un bouton ou même de fermer la fenêtre jusqu’à la fin de cette animation.

Avec le module tkinter, on dispose d’une méthode applicable à la fenêtre qui va nous permettre de déclencher une fonction après un délai précisé en milli-secondes et qui a l’avantage de ne pas bloquer les autres processus (rafraîchissement de l’écran, détection des actions de la souris ou du clavier…)

On procède de la manière suivante :

fenetre.after(delai, fonction)

Exemple 19


# -*-coding:Latin-1 -*
from tkinter import *
import time
def affiche() :
f = "Nous sommes le %d/%m/%y, il est %H:%M:%S"
lbl.config(text=time.strftime(f))
fenetre.after(500,affiche)
fenetre = Tk( )
fenetre.geometry("300x50")
lbl = Label(fenetre, text= " heure ")
lbl.place(x = 10 , y = 10)
affiche( )
fenetre.mainloop()

Quel est le rôle de ce programme ?

Remarque

Comme pour l’option command d’un bouton, dans after, on indique seulement le nom de la fonction à appeler sans paramètre ni parenthèse. Si vous en mettez, vous entrez dans une boucle récursive infinie, qui se terminera par un beau plantage du moteur Python …

Animation d’un personnage :

Comme nous l’avons déjà remarqué aux paragraphes précédents, tkinter ne sait pas afficher des images gif animées, cependant on peut décomposer un gif animé en une succession d’images gif et les afficher à tour de rôle pour reproduire l’animation. Le logiciel de dessin libre GIMP peut faire cela.

Exemple 20


from tkinter import *
def anim():
    #Fonction permettant le déplacement de l'image
    global x, img
    x = x + 40
    if x > 1000 : x = -40
    img = PhotoImage(file="cocD.gif")
    Fond.itemconfig(positImg, image = img)
    Fond.coords(positImg,x,120)
    fenetre.after(500,anim)

fenetre=Tk()
fenetre.title("La course d'une coccinelle")

Fond=Canvas(fenetre,width=1000,height=300,bg="white")
Fond.grid()

img = PhotoImage(file="cocD.gif")
positImg = Fond.create_image(-40,0,image=img,anchor='nw')
# Initialisation de la position de l'image sur l'axe horizontal
x = -40
#Appel de la fonction animation() pour le déplacement de l'image
anim()
fenetre.mainloop()

Quel est le rôle de ce programme ?