Code source de pydiderotlibs.graphique

# -*- coding: utf-8 -*-
#

# import des librairies
from time import sleep  # pour rendre transparent côté élève l'utilisation de "sleep"
from random import randint #pour la fonction vecteur2 et pour cercle_aleatoire
from .detection_collision import *

import sys
import math
import pygame
from .couleurs import rgb
from .touches_pygames import TOUCHES_PYGAMES
# Un petit message invitant a lire la doc
print("""
Merci d'utiliser la librairie graphique du module pydiderotlibs.\n
N'hésitez pas à consulter la documentation en ligne:\n
https://pydiderotlibs.rtfd.io/librairies/graphique.html
""")

# On définit les variables globales.
# Si quelqu'un veut s'amuser à sous-classer pygame.Surface pour faire plus propre il est bienvenue
_axeMath = False
_fenetre = None
_autorefresh = True


def creer_fenetre(largeur=600, hauteur=600, orientation_axe_ordonnees=False, titre="Fenetre graphique", autorefresh=True):
    fenetre(largeur, hauteur, orientation_axe_ordonnees, titre, autorefresh)


def window(largeur=600, hauteur=500, orientation_axe_ordonnees=False, titre="Fenetre graphique", autorefresh=True):
    fenetre(largeur, hauteur, orientation_axe_ordonnees, titre, autorefresh)


[docs]def fenetre( largeur=600, hauteur=500, orientation_axe_ordonnees=False, titre="Fenetre graphique", autorefresh=True ): """ Crée et affiche une fenêtre graphique. Alias: ``window()``, ``creer_fenetre()`` Arguments: largeur (int, optionnel): Largeur de la fenetre en pixels (``600`` par défaut) hauteur (int, optionnel): Hauteur de la fenetre en pixels (``500`` par défaut) orientation_axe_ordonnees: Si on met cet argument à True, l'axe des ordonnées sera orienté de bas en haut comme en maths. Sinon il est orienté dans l'autre sens comme habituellement en informatique (``False`` par défaut) titre (str, optionnel): Titre de la fenetre (``Fenetre graphique`` par défaut) autorefresh(bool, optionnel): Active le rafraichissement automatique de la fenetre graphique (`False` par défaut) """ pygame.init() global _fenetre global _axeMath global _autorefresh _axeMath = orientation_axe_ordonnees _autorefresh = autorefresh _fenetre = pygame.display.set_mode((largeur, hauteur)) pygame.display.set_caption(titre) # active la répétition des touches pygame.key.set_repeat(1) _fenetre.fill(rgb('blanc')) pygame.display.update()
def _ordo(y): global _axeMath ymax = pygame.display.Info().current_h if _axeMath: return ymax-y return y
[docs]def rafraichir(): """ Rafraîchie la fenêtre graphique. C'est uniquement utile si vous désactivez l'option :ref:`autorefresh <autorefresh>`. """ pygame.display.update()
def ecoute_evenements(): return demande_evenements() def events(): return demande_evenements()
[docs]def demande_evenements(): """ Récupère les évenements pygame gère la fermeture de la fenetre et retourne les évenements formatés. Renvoie un dictionnaire d'évenements formaté comme suit: ``{'touche1': None, 'touche2':None, 'souris': [x,y], 'click': [x,y]}`` Les valeurs ``None`` pour les touches peuvent surprendre mais il est nécéssaire d'utiliser un dictionnaire pour avoir les coordonnées éventuelles de la souris lors d'un click par exemple. Pour les touches clavier, l'importance est la présence de la cléf et la valeur associée est donc ``None``. - Les caractères alphanumériques sont encodés en ascii (``'a'``, ``'n'``, ``';'``) et, si présent, leur valeur est ``None``. - les touches spéciales ont les clefs ``'espace'``, ``'haut'``, ``'bas'``, ``'droite'``, ``'gauche'`` et, si présent, leur valeur est ``None``. - Un clic avec le bouton gauche de la souris ajoute une clef ``'clic'``. Sa valeur est une liste ``[x, y]`` des coordonnées de la souris. - Un déplacement de la souris ajoute une clef ``'souris'``. Sa valeur est une liste ``[x, y]`` des coordonnées de la souris. Alias: ``events()``, ``ecoute_evenements()`` """ # Initialisation du dictionnaire de sortie evenements = {} for event in pygame.event.get(): # Gestion de la fermeture de la fenetre if event.type == pygame.QUIT: sys.exit() # Gestion des evenements souris elif event.type == pygame.MOUSEMOTION: evenements['souris'] = list(event.pos) elif event.type == pygame.MOUSEBUTTONDOWN: evenements['clic'] = list(event.pos) # Gestion des touches. pygame.KeyDown ne va pas gérer plusieurs touches enfoncées # https://www.pygame.org/docs/ref/key.html#pygame.key.get_pressed touches = pygame.key.get_pressed() for touche, k_touche in TOUCHES_PYGAMES.items(): if touches[getattr(pygame, k_touche)]: evenements[touche] = None return evenements
[docs]def efface(couleur='blanc'): """ Efface l'écran. Arguments: couleur (:ref:`couleur <couleur>`, optionnel): Couleur de remplissage de l'écran (``'blanc'`` par défaut). """ _fenetre.fill(rgb(couleur)) if _autorefresh: pygame.display.update()
def erase(couleur='blanc'): efface(couleur) def trace_cercle(x, y, couleur='bleu', rayon=25, epaisseur=0): cercle(x, y, couleur, rayon, epaisseur) def circle(x, y, couleur='bleu', rayon=25, epaisseur=0): cercle(x, y, couleur, rayon, epaisseur)
[docs]def cercle(x, y, couleur='bleu', rayon=25, epaisseur=0): """ Trace un cercle dans la fenetre graphique. Alias: ``circle()``, ``trace_cercle()`` Arguments: x (int): Abscisse du centre du cercle y (int): Ordonnée du centre du cercle rayon (int, optionnel): Rayon du cercle (25 par défaut) epaisseur (int, optionnel): Epaisseur du cercle (``0`` par défaut). Si ``0``, le cercle sera rempli et apparaitra comme un disque. couleur (:ref:`couleur <couleur>`, optionnel): Couleur du cercle (bleu par défaut) """ couleur = rgb(couleur) pygame.draw.circle(_fenetre, couleur, (x, _ordo(y)), rayon, epaisseur) if _autorefresh: pygame.display.update()
def trace_cercle_aleatoire(couleur='bleu', rayon=5, epaisseur=0): cercle_aleatoire(couleur, rayon, epaisseur) def random_circle(couleur='bleu', rayon=5, epaisseur=0): cercle_aleatoire(couleur, rayon, epaisseur) def randcircle(couleur='bleu', rayon=5, epaisseur=0): cercle_aleatoire(couleur, rayon, epaisseur)
[docs]def cercle_aleatoire(couleur='bleu', rayon=5, epaisseur=0): """ Trace un (petit) cercle dans la fenetre graphique, à un endroit choisit au hasard. (Utile pour fairre de la neige par exemple.) Alias: ``random_circle()``, ``randcircle()``, ``trace_cercle_aleatoire()`` Arguments: rayon (int, optionnel): Rayon du cercle (5 par défaut) epaisseur (int, optionnel): Epaisseur du cercle (``0`` par défaut). Si ``0``, le cercle sera rempli et apparaitra comme un disque. couleur (:ref:`couleur <couleur>`, optionnel): Couleur du cercle (bleu par défaut) """ couleur = rgb(couleur) ymax = pygame.display.Info().current_h xmax = pygame.display.Info().current_w centre = (randint(-rayon, xmax + rayon),randint(-rayon, ymax + rayon)) pygame.draw.circle(_fenetre, couleur, centre, rayon, epaisseur) if _autorefresh: pygame.display.update()
def trace_point(x, y, couleur='bleu'): point(x, y, couleur)
[docs]def point(x, y, couleur='bleu'): """ Trace un point dans la fenetre graphique. Alias: ``trace_point()`` Arguments: x (int): Abscisse du point y (int): Ordonnée du point couleur (:ref:`couleur <couleur>`, optionnel): Couleur du point (bleu par défaut) """ couleur = rgb(couleur) pygame.draw.circle(_fenetre, couleur, (x, _ordo(y)), 1, 0) if _autorefresh: pygame.display.update()
def trace_rectangle(x, y, largeur=100, hauteur=50, couleur='bleu', epaisseur=0): rectangle(x, y, largeur, hauteur, couleur, epaisseur)
[docs]def rectangle(x, y, largeur=100, hauteur=50, couleur='bleu', epaisseur=0): """ Trace un rectangle horizontal dans la fenetre graphique . Le sommet haut-gauche à pour coordonnées ``(x,y)``, la ``largeur`` est la taille en abscisse et la ``hauteur`` la taille en ordonnée. Alias: ``trace_rectangle()`` Arguments: x (int): abscisse du sommet haut gauche du rectangle y (int): ordonnée du sommet haut gauche du rectangle largeur (int): taille du rectangle sur l'axe des abscisses hauteur (int): taille du rectangle sur l'axe des ordonnées couleur (:ref:`couleur <couleur>`, optionnel): Couleur du rectangle (``bleu`` par défaut) epaisseur (int, optionnel): Epaisseur des cotés du rectangle (``0`` par défaut). Si ``0``, le rectangle est rempli. """ couleur = rgb(couleur) pygame.draw.rect(_fenetre, couleur, (x, _ordo(y), largeur, hauteur), epaisseur) if _autorefresh: pygame.display.update()
def trace_triangle(x1, y1, x2, y2, x3, y3, couleur='bleu', epaisseur=0): triangle(x1, y1, x2, y2, x3, y3, couleur, epaisseur)
[docs]def triangle(x1, y1, x2, y2, x3, y3, couleur='bleu', epaisseur=0): """ Trace un triangle dans la fenetre graphique . Alias: ``trace_triangle()`` Arguments: x1 (int): abscisse du premier sommet du triangle y1 (int): ordonnée du premier sommet du triangle x2 (int): abscisse du deuxième sommet du triangle y2 (int): ordonnée du deuxième sommet du triangle x3 (int): abscisse du troisième sommet du triangle y3 (int): ordonnée du troisième sommet du triangle couleur (:ref:`couleur <couleur>`, optionnel): Couleur du triangle (``bleu`` par défaut) epaisseur (int, optionnel): Epaisseur des cotés du triangle (``0`` par défaut). Si ``0``, le triangle est rempli. """ couleur = rgb(couleur) pygame.draw.polygon(_fenetre, couleur, [(x1, _ordo(y1)), (x2, _ordo(y2)), (x3, _ordo(y3))], epaisseur) if _autorefresh: pygame.display.update()
def trace_segment(x1, y1, x2, y2, couleur='bleu', epaisseur=2): segment(x1, y1, x2, y2, couleur, epaisseur)
[docs]def segment(x1, y1, x2, y2, couleur='bleu', epaisseur=2): """ Trace un segment entre les points de coordonées ``(x1, y1)`` et ``(x2, y2)``. Alias: ``trace_segment()`` Arguments: x1 (int): abscisse de la première extremité du segment y1 (int): ordonnée de la première extremité du segment x2 (int): abscisse de la deuxieme extrémité du segment y2 (int): ordonnée de la deuxieme extrémité du segment couleur (:ref:`couleur <couleur>`, optionnel): Couleur du segment (``bleu`` par défaut) epaisseur (int, optionnel): Epaisseur du segment (``2`` par défaut) """ couleur = rgb(couleur) pygame.draw.lines(_fenetre, couleur, False, [(x1, _ordo(y1)), (x2, _ordo(y2))], epaisseur) if _autorefresh: pygame.display.update()
def trace_vecteur(x, y, v, couleur='rouge', epaisseur=2): vecteur(x, y, v, couleur, epaisseur) def vector(x, y, v, couleur='rouge', epaisseur=2): vecteur(x, y, v, couleur, epaisseur)
[docs]def vecteur(x, y, v, couleur='rouge', epaisseur=2): """ Trace la représentation du vecteur ``v`` à partir du point d'origine ``(x, y)``. Alias: ``vector()``, ``trace_vecteur()`` Arguments: x (int): abscisse du point d'origine de la représentation du vecteur y (int): ordonnée du point d'origine de la représentation du vecteur v (list): Coordonnées de la deuxieme extrémité du segment couleur (:ref:`couleur <couleur>`, optionnel): Couleur du segment (``rouge`` par défaut) epaisseur (int, optionnel): Epaisseur du segment (``2`` par défaut) """ couleur = rgb(couleur) trace_segment(x, y, x + v[0], y + v[1], couleur, epaisseur) w1 = [0, 0] w2 = [0, 0] w1[0] = -.3 * math.cos(15 * math.pi / 180) * v[0] + .3 * math.sin(15 * math.pi / 180) * (-v[1]) w1[1] = -.3 * math.cos(15 * math.pi / 180) * v[1] + .3 * math.sin(15 * math.pi / 180) * v[0] w2[0] = -.3 * math.cos(15 * math.pi / 180) * v[0] - .3 * math.sin(15 * math.pi / 180) * (-v[1]) w2[1] = -.3 * math.cos(15 * math.pi / 180) * v[1] - .3 * math.sin(15 * math.pi / 180) * v[0] pygame.draw.polygon( _fenetre, couleur, [ (x + v[0], _ordo(y + v[1])), (x + v[0] + w1[0], _ordo(y + v[1] + w1[1])), (x + v[0] + w2[0], _ordo(y + v[1] + w2[1])), (x + v[0], _ordo(y + v[1])) ], 0 ) if _autorefresh: pygame.display.update()
[docs]def vecteur2(xv, yv, couleur='rouge', epaisseur=2): """ Trace la représentation du vecteur de coordonnées ``(xv, yv)`` à partir d'une origine choisie au hasard. Alias: ``vector2()``, ``trace_vecteur2()`` Arguments: xv (int): abscisse du vecteur yv (int): ordonnée du vecteur couleur (:ref:`couleur <couleur>`, optionnel): Couleur du segment (``rouge`` par défaut) epaisseur (int, optionnel): Epaisseur du segment (``2`` par défaut) """ ymax = pygame.display.Info().current_h xmax = pygame.display.Info().current_w x = randint(xv, xmax - xv) y = randint(yv, ymax - yv) v = [xv, yv] vector(x, y, v, couleur, epaisseur)
def trace_vecteur2(xv, yv, couleur='rouge', epaisseur=2): vecteur2(xv, yv, couleur, epaisseur) def vector2(xv, yv, couleur='rouge', epaisseur=2): vecteur2(xv, yv, couleur, epaisseur) def trace_image(x, y, nom, largeur=100, hauteur=100): image(x, y, nom, largeur, hauteur)
[docs]def image(x, y, nom, largeur=100, hauteur=100): """ Trace une image dans la fenetre graphique. Alias:``trace_image()`` Arguments: x (int): Abscisse du centre de l'image y (int): Ordonnée du centre de l'image nom (str) : nom du fichier image (qui doit être dans le répertoire du script) largeur (int, optionnel): Largeur de l'image (100 par défaut) hauteur (int, optionnel): Hauteur de l'image (100 par défaut) """ pygame_image = pygame.transform.scale(pygame.image.load( nom).convert_alpha(), (largeur, hauteur)) _fenetre.blit(pygame_image, (int(x - largeur / 2), _ordo(int(y - hauteur / 2)))) if _autorefresh: pygame.display.update()
def trace_explosion(x, y, couleur='orange', r=50, c=0.5, n=10): explosion(x, y, couleur, r, c, n)
[docs]def explosion(x, y, couleur='orange', r=25, c=0.5, n=10): ''' Trace un polygône régulier étoilé à ``2n`` côté, de rayon extérieur ``r``, et tel que le rayon intérieur est égal à ``c*r`` (pour ``c=0``, le polygône est réduit à ``n`` rayons du cencle de rayon ``r`` pour ``c=1``, c'est un polygône régulier à ``2n`` côtés) Alias: ``trace_explosion()`` Arguments: x (int): Abscisse du centre de l'explosion y (int): Ordonnée du centre de l'explosion couleur (:ref:`couleur <couleur>`, optionnel): Couleur (``orange`` par défaut) r (int): Rayon extérieur c (float):Coefficient pour obtenir le rayon intérieur égal à ``c*r`` n (int): Nombre de sommets ''' couleur = rgb(couleur) pointlist = [] theta = 2 * math.pi / n for k in range(n): pointlist.append(( x + r * math.cos(k * theta), _ordo(y + r * math.sin(k * theta)) )) pointlist.append(( x + c * r * math.cos((k + 1 / 2) * theta), _ordo(y + c * r * math.sin((k + 1 / 2) * theta)) )) pointlist.append((x + r, _ordo(y))) pygame.draw.polygon(_fenetre, couleur, pointlist) if _autorefresh: pygame.display.update()
def trace_axes(color='noir'): axes(color)
[docs]def axes(color='noir'): ''' Dessine les axes de coordonnées pour une meilleure compréhension par les élèves. Alias: ``trace_axes()`` ''' couleur = rgb(color) ymax = pygame.display.Info().current_h xmax = pygame.display.Info().current_w epaisseur = 2 correction = 0 if _axeMath: ## cette correction vient du fait que les textes continuent d'être définis par rapport à leur coin haut-gauche même si l'axe est à l'envers correction = 12 pygame.draw.lines(_fenetre, couleur, False, [(5, _ordo(0)), (5, _ordo(ymax))], epaisseur) pygame.draw.lines( _fenetre, couleur, False, [ (0, _ordo(ymax - 5)), (5, _ordo(ymax))], epaisseur) pygame.draw.lines( _fenetre, couleur, False, [ (10, _ordo(ymax - 5)), (5, _ordo(ymax))], epaisseur) pygame.draw.lines(_fenetre, couleur, False, [(0, _ordo(5)), (xmax, _ordo(5))], epaisseur) pygame.draw.lines( _fenetre, couleur, False, [ (xmax - 5, _ordo(0)), (xmax, _ordo(5))], epaisseur) pygame.draw.lines( _fenetre, couleur, False, [ (xmax - 5, _ordo(10)), (xmax, _ordo(5))], epaisseur) font = pygame.font.Font(None, 24, bold=False, italic=False) text = font.render(str(ymax), 1, couleur) _fenetre.blit(text, (15, _ordo(ymax - 35 + correction))) text = font.render("y", 1, couleur) _fenetre.blit(text, (15, _ordo(ymax - 17 + correction))) text = font.render(str(xmax), 1, couleur) _fenetre.blit(text, (xmax - 35, _ordo(10 + correction))) text = font.render("x", 1, couleur) _fenetre.blit(text, (xmax - 15, _ordo(25 + correction))) text = font.render("0", 1, couleur) _fenetre.blit(text, (10, _ordo(10 + correction))) if _autorefresh: pygame.display.update()
# Gestion du texte. # voir https://nerdparadise.com/programming/pygame/part5 # On stocke les polices dans une globale pour ne pas avoir à la regénérer à chaque affichage. _cached_fonts = {} # On stocke les textes sous forme d'images pour ne pas avoir à la regénérer à chaque affichage. # Un meme texte a de grande chance d'être affiché plusieurs fois. _cached_text = {} def _make_font(fonts, size): """ Initialise une police de caractères à partir d'une liste. On retourne la première police de la liste installée sur la machine. Si aucune police n'est installée, on retourne la police systeme par défaut. Arguments: fonts(list): Liste de polices de caractères size(int): Taille de la police de caractère Returns: la première police de la liste installée sur la machine de taille ``size``. """ available = pygame.font.get_fonts() # get_fonts() returns a list of lowercase spaceless font names choices = map(lambda x:x.lower().replace(' ', ''), fonts) for choice in choices: if choice in available: return pygame.font.SysFont(choice, size) return pygame.font.Font(None, size) def _get_font(font_preferences, size): """ Retourne une police de caractère dans la liste ``font_preferences`` de taille ``size`` Si la police a déja été utilisée on utilise la cache ``_cached_fonts``. """ global _cached_fonts key = str(font_preferences) + '|' + str(size) font = _cached_fonts.get(key, None) if font is None: font = _make_font(font_preferences, size) _cached_fonts[key] = font return font def _create_text(text, fonts, size, color): global _cached_text key = '|'.join(map(str, (fonts, size, color, text))) text_image = _cached_text.get(key, None) if text_image is None: font = _get_font(fonts, size) text_image = font.render(text, True, color) _cached_text[key] = text_image return text_image
[docs]def texte(message, x, y, police='', taille=12, couleur="noir"): """ Affiche un texte dans la fenetre graphique. Arguments: message(str): le texte à afficher x(int): abscisse du début du texte y(int): ordonnée du haut du début du texte police(str, optionnel): la police de caractère à utiliser. Si non renseigné ou si la police n'est pas installée, on utilise la police de caractère défaut du system taille(int, optionnel): taille du texte couleur(:ref:`couleur <couleur>`, optionnel): couleur du texte """ texte_image = _create_text(message, [police], taille, rgb(couleur)) pygame.display.get_surface().blit(texte_image, (x, y)) if _autorefresh: pygame.display.update()
[docs]def polices_disponibles(): """ Retourne les polices caractères disponibles """ return pygame.font.get_fonts()
[docs]def test_police(police): """ Test si une police de caractère est installée. Arguments: police(str): police de caractère à tester Returns: True si la police est installée, False sinon """ return police.lower().replace(' ', '') in polices_disponibles()