Graphismes (et interaction) en Python avec Pygame

Graphismes (et interaction) en Python avec Pygame #

\(\)

Ce module n’est plus utilisé. Voir les mises à jour ici : Travailler avec des images en Python.

Comme en C, il existe de multiples manières de créer et manipuler des images en Python.

Nous allons ici traiter du module pygame, une interface à la bibliothèque SDL, très complet Vous aurez besoin de télécharger un module complémentaire, à placer dans le même répertoire que votre fichier :

Le module lspg (qui ne contient pas grand chose) est à télécharger séparément ici : lspg.py. Vous pourrez ensuite le copier dans votre répertoire de travail.

import pygame
import pygame.gfxdraw
import lspg

pygame.init()
xx, yy = 200, 200
size = [xx, yy]
screen = pygame.display.set_mode(size)

for x in range(xx):
    for y in range(yy):
        c = [x * 255 / xx, y * 255 / yy, (x + y) * 255 / (xx + yy)]
        pygame.gfxdraw.pixel(screen, x, y, c)
    pygame.display.flip()

lspg.lspgloop()
pygame.quit()

La documentation de PyGame est très fournie :

Il est difficile de ne pas se noyer dans la documentation de PyGame. Dans un premier temps, vous devez être capables d’ouvrir une fenêtre, de faire des points, des lignes, des rectangles et des cercles de couleur.

Les deux lignes : import lspg et lspg.lspgloop() ne sont pas distribuées avec Pygame. Il s’agit d’un petit module qui vous simplifiera l’utilisation de PyGame au début. La méthode lspg.lspgloop() permet simplement de laisser la fenêtre ouverte, et d’attendre que l’utilisateur la referme pour passer à la suite et donc terminer le programme.

Fonction utiles #

Créer une surface 400x400 à l’écran :

screen = pygame.display.set_mode((400, 400))

Récupérer la largeur et la hauteur d’une surface :

larg = screen.get_width()
haut = screen.get_height()

Tracer un carré de coin supérieur gauche (100,120) et de côté 10 en bleu :

pygame.gfxdraw.box(screen,(100, 120, 10, 10), [0, 0, 255])

Tracer une droite du point (10, 10) au point (100, 120), en rouge :

pygame.gfxdraw.line(screen, 10, 10, 100, 120, [255, 0, 0])

Pour effacer l’écran, on peut dessiner un rectangle sur toute sa surface, on bien faire :

screen.fill((0, 0, 0))

Vous pouvez sauvegarder dans un fichier le contenu d’une surface ainsi :

pygame.image.save(screen, 'nomfichier.bmp')

La sauvegarde au format BMP marche toujours. La sauvegarde dans d’autres format n’est fonctionnelle que sur certaines plates-formes, en présence de certaines bibliothèques. À vous de vérifier, après une sauvegarde, que celle-ci est correcte.

On considère la courbe paramétrique suivante :

\[\left\{ \begin{array}{l} x(t)=\cos(0.0015,t)\\ y(t)=\sin(k,t)\\ \end{array} \right.\]

On souhaite tracer cette courbe dans une fenêtre 512x512, de manière à ce que la figure occupe toute la fenêtre, pour \(k=0.0045\) et \(t\), nombre entier variant de 0 à 10000.

Une fois ce tracé obtenu, faites varier la valeur de \(k\).

Gestion des évènements avec PyGame #

Le principe consiste à écrire une boucle de gestion des évènements (boucle while not done ci-dessous). Dans cette boucle, chaque événement sont examinés (variable event). Selon leur type (pygame.QUIT, pygame.MOUSEBUTTONDOWN), on provoque l’exécution de telle ou telle procédure.

Gestion des clics souris #

Dans le cas de l’évènement pygame.MOUSEBUTTONDOWN, l’événement contient, dans le champ ‘event.dict’ un dictionnaire avec les entrées:

  • pos : tuple de deux valeurs contenant les coordonnées cliquées
  • button : numéro du bouton souris cliqué
import pygame
import pygame.draw

def clic(screen, coord, b):
    """ Procédure appelée en cas de clic
    à la souris. Elle a pour effet d'afficher
    un point de couleur (la couleur dépend du
    bouton souris utilisé) à l'endroit cliqué
    """
    print("Clic en", coord[0], coord[1])
    print("Bouton", b)
    if b == 1:
        c = [255, 255, 0]
    elif b==2:
        c = [255, 0, 255]
    else:
        c = [0, 255, 255]
    pygame.draw.circle(screen, c, coord, 5, 0)
    pygame.display.flip()


def main() :
    pygame.init()
    xx, yy = 200, 200
    size = [xx, yy]
    screen = pygame.display.set_mode(size)
  
    #########################
    # BOUCLE DES ÉVÈNEMENTS
    #########################
    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done=True
            if event.type == pygame.MOUSEBUTTONDOWN:
                # Dans le cas d'un clic souris, 
                # event.dict['pos'] contient les coordonnées
                # event.dict['button'] contient le numéro 
                # du bouton souris
                clic(screen, event.dict['pos'], event.dict['button'])
  
    #################################
    # FIN DE LA BOUCLE DES ÉVÈNEMENTS
    #################################
    pygame.quit()

if __name__ == '__main__':
    main()

Gestion du clavier #

Dans le cas de l’évènement pygame.KEYDOWN, l’événement contient, dans le champ ‘event.dict’ un dictionnaire avec les entrées :

  • key : numéro de la touche
  • unicode : caractère
  • modifier : combinaisons Alt, Shift, Ctrl…

Le programme suivant permet d’examiner les évènements MOUSEBUTTONDOWN et KEYDOWN.

import pygame
import pygame.draw

def main() :
    pygame.init()
    xx, yy = 200, 200
    size = [xx, yy]
    screen = pygame.display.set_mode(size)
    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            if event.type in (pygame.MOUSEBUTTONDOWN, pygame.KEYDOWN):
                print('==> Evenement :', event.type)
                for k, v in event.dict.items():
                    print(k, v)
                print()

    pygame.quit()

if __name__ == '__main__' :
    main()

Ajout d’une fonction qui s’exécute en tâche de fond #

Il est possible d’insérer, dans la boucle des événements, l’exécution d’une fonction particulière. Étant exécutée régulièrement, cette fonction pourra s’occuper, par exemple, du rafraîchissement de la fenêtre et des animations.

Une telle fonction doit s’exécuter régulièrement, très souvent, mais très rapidement aussi car durant son exécution, l’interface sera gelée (plus de réponse aux événements etc…).

Voyons comment intégrer une telle fonction, que nous appellerons ici idle, à la boucle des événements:

clock = pygame.time.Clock()
done = False
while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done=True
        # autre gestion des événements...
    idle()  # exécution de la tâche de fond
    clock.tick(20) # mais pas trop vite....

pygame.quit()

Les lignes relatives à clock sont utilisée pour ralentir l’animation. On s’assure ici qu’il n’y aura pas plus de 20 tours de boucle par seconde. Dans un premier temps, vous pouvez supprimer ces lignes.