Définir une classe Date avec trois attributs:
Lorsqu'un attribut concerne tous les objets d'une même classe, on parle d'attribut de classe
Ainsi pour la lisibilité des objets on va introduire un attribut de classe à la classe Date, sous la forme d'une liste nom_mois, permettant l'affichage d'une date sous la forme "15 septembre 2021" plutôt que 15/09/21
class Date:
nom_mois = ['#', 'janvier','février',...]
#à compléter
def __init__(self,j,m,a):
pass
Cet attribut de classe peut être utilisé sous la forme Date.nom_mois
Avec les méthodes suivantes :
On peut vouloir comparer des dates. Python propose une méthode particulière à définir pour cela __lt__(self,other) qui renvoie Vrai si l'objet en cours de construction self est strictement plus petit que la date other
Compléter le code suivant et tester le
def __lt__(self,other):
return self.annee < other.annee or\
(self.annee == other.annee and self.mois < other.mois) or\
#... à compléter
Il existe aussi des méthodes qui ne s'appliquent pas à un objet en particulier mais servent de fonction d'aide pour tous les objets définis par la classe
On peut soit définir cette fonction à l'extérieur de la classe et l'appeler à l'intérieur de la classe où l'insérer dans la classe sans self dans les paramètres
Compléter le code suivant
def max(date1,date2):
if date1.annee > date2.annee :
return date1
elif date2.annee > date1.annee :
return date2
elif date1.mois > date1.mois :
# à compléter
Si on appelle cette méthode de classe à l'extérieur de la classe on procède ainsi par exemple
date1 = Date(12,09,2021)
date2 = Date(12,03,2022)
print(Date.max(date1,date2))
A quel moment contrôle-t-on la validité des données?
La classe Date telle que l'on a écrit précedemment suppose que l'on a "filtré" les dates avant de les insérer dans le constructeur
Si on a oublié de le faire on peut se retrouver avec des dates comme 32/15/2021 !
Pire les valeurs ne sont peut être pas entières!
Nous sommes habitués aux messages d'erreur dans la console
S'il y a plusieurs erreurs dans un programme, une erreur de syntaxe ou une erreur de calcul, le programme s'arrête à la première erreur rencontrée
Dans le langage Python un certain nombre d'erreurs est déjà répertorié comme par exemple une division par zéro
Dans notre cas nous devons définir ce qui est invalide et sera considéré comme une erreur qui arrêtera le programme dès son interception
Dans un premier temps on va définir une méthode de classe jour_est_valide(j) où on teste si le jour est de type entier et est compris entre 1 et 31, si ce n'est pas le cas on lève une erreur qui est un objet de type ValueError avec un message approprié "le jour "+ str(j) + " est invalide" passé en paramètre du constructeur de l'objet ValueError
class Date2:
#attribut de classe
nom_mois = ["#",'janvier','fevrier','mars','avril','mai',
'juin','juillet','août','septembre','octobre','novembre','décembre']
#méthodes de classe
def jour_est_valide(j):
return isinstance(j,int) and (1 <= j <= 31)
Ensuite dans le constructeur on écrit
def __init__(self,j,m,a):
if Date2.jour_est_valide(j):
self.jour = j
else:
raise ValueError("le jour "+ str(j) + " est invalide")
Compléter le code pour tester le mois et l'année puis ensuite réaliser les tests suivants
date1 = Date2((2,1),1,2022)
date2 = Date2("Lundi",1,2022)
date3 = Date2("Lundi",15,2022)
On observe que le programme s'arrête dès la première erreur car le jour est de type tuple au lieu de int, les deux dernières instructions ne sont pas exécutées
Pour que le programme soit exécuté dans son intégralité malgré les erreurs rencontrées on encadre les sources d'erreurs possibles par le bloc try ...except dans le constructeur
class Date3:
#attribut de classe
nom_mois = ["#",'janvier','fevrier','mars','avril','mai',
'juin','juillet','août','septembre','octobre','novembre','décembre']
#méthode de classe
def jour_est_valide(j):
return isinstance(j,int) and (1 <= j <= 31)
def __init__(self,j,m,a):
try:
if Date3.jour_est_valide(j):
self.jour = j
else:
raise ValueError
except:
print("le jour "+ str(j) + " est invalide")
Compléter le constructeur et réaliser les mêmes tests que précédemment et observer que cette fois ci le programme ne s'arrête pas et délivre tous les messages d'erreur d'un coup
Un système d'exploitation propose de trier les fichiers contenus dans un répertoire par différentes entrées possibles(en simplifiant):
Définir une classe Fichier, les attributs sont nom de type str, date de type Date (voir plus haut) et taille de type int
On commence par définir une méthode __lt__(self,other) qui retourne vrai si un fichier est strictement plus petit qu'un autre suivant la date d'ajout
On rappelle qu'en Première on a implémenté le tri par insertion pour des entiers ou des chaînes de caractères
def insertion(T,i):
"""
La liste T contient des entiers
ou des chaines de caractères
est triée de l'indice 0
jusqu'à l'indice i - 1
i >= 1
On insère T[i] à sa place et à la fin
La liste T est triée de l'indice 0
jusqu'à l'indice i
"""
temp = T[i]
j = i
while j > 0 and T[j-1] > temp:
T[j] = T[j-1]
j -= 1
T[j] = temp
def tri_insertion(T):
"""
tri par insertion la liste T
contenant des nombres ou des chaines de caractères
"""
for i in range(1,len(T)):
insertion(T,i)
nous allons l'adapter pour des fichiers avec le comparateur défini ci-dessus
def insertion(T,i):
"""
La liste T contient des fichiers
comparables suivant leur date d'ajout
est triée de l'indice 0
jusqu'à l'indice i - 1
i >= 1
On insère T[i] à sa place et à la fin
La liste T est triée de l'indice 0
jusqu'à l'indice i
"""
temp = T[i]
j = i
#on peut utiliser > à cause de __lt__()
while j > 0 and T[j-1] > temp:
T[j] = T[j-1]
j -= 1
T[j] = temp
def tri_insertion(T):
"""
tri par insertion la liste T
contenant des nombres ou des chaines de caractères
"""
for i in range(1,len(T)):
insertion(T,i)
On n'a résolu que partiellement le problème car on ne peut pas utiliser la fonction tri_insertion(T) pour trier les fichiers suivant leur taille ou leur nom
Pour plus de généralité on va donc créer un objet Comparateur ayant un attribut nom, valant soit 'taille', soit 'nom' et soit 'date'
Cet objet n'a qu'une seule méthode plus_petit(self,x,y) qui en fonction du nom du comparateur compare les fichiers x et y avec l'opérateur <
Ce qui suppose que pour un attribut d'un autre type que int,float ou str la méthode __lt__() ait été implémentée pour donner un sens à l'opérateur <
class Comparateur:
def __init__(self,nom):
self.nom = nom
def plus_petit(self,x,y):
if self.nom== 'taille':
return x.taille < y.taille
if self.nom == 'date':
return x.date < y.date
if self.nom == 'nom':
return x.nom < y.nom
Maintenant on peut trier les fichiers suivant n'importe quel attribut du fichier en ajoutant comme paramètre à insertion(T,i) et tri_insertion(T) un comparateur comp
def insertion(T,i,comp):
"""
La liste T est triée de l'indice 0
jusqu'à l'indice i - 1
i >= 1
On insère T[i] à sa place et à la fin
La liste T est triée de l'indice 0
jusqu'à l'indice i
"""
temp = T[i]
j = i
while j > 0 and comp.plus_petit(temp,T[j-1]):
T[j] = T[j-1]
j -= 1
T[j] = temp
def tri_insertion(T,comp):
"""
tri par insertion la liste T
contenant des nombres ou des chaines de caractères
"""
for i in range(1,len(T)):
insertion(T,i,comp)
A l'exécution on peut avoir par exemple
date1 = Date(15,1,2022)
date2 = Date(17,1,2022)
date3 = Date(15,2,2022)
fic1 = Fichier('programme1',date1,1000)
fic2 = Fichier('texte1',date2,300)
fic3 = Fichier('image',date3,100)
c1 = Comparateur('nom')
c2 = Comparateur('date')
c3 = Comparateur('taille')
liste_fichiers = [fic1,fic2,fic3]
#tri suivant le nom
tri_insertion(liste_fichiers,c1)
#tri suivant la date
tri_insertion(liste_fichiers,c2)
#tri suivant la taille
tri_insertion(liste_fichiers,c3)
Pygame est un module de Python permettant de manipuler des images et des sons dans le but de réaliser un jeu
Par exemple tester celui-ci
import pygame.examples.aliens
pygame.examples.aliens.main()
A travers des exemples nous allons découvrir les classes de Pygame
Le premier programme crée une fenêtre graphique de 400 x 200 ayant un fonds noir
Cependant on ne peut pas fermer la fenêtre
#importation de librairies
import pygame
#-----------------Constantes-----------------------------------------
LARGEUR = 400
HAUTEUR = 200
NOIR = (0,0,0)
#Initialisation de pygame
pygame.init()
#Création de la fenetre graphique
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation0")
fenetre.fill(NOIR)
Explications
Observez qu'on ne peut pas fermer la fenêtre et que Pygame est toujours actif
Ajouter à la fin quit(). Que se passe-t-il ?
On veut quitter proprement Pygame et pouvoir fermer la fenêtre.On considère le fait de vouloir fermer la fenêtre comme un évènement à capturer
#importation de librairies
import pygame
#-----------------Constantes-----------------------------------------
LARGEUR = 400
HAUTEUR = 200
NOIR = (0,0,0)
#Initialisation de pygame
pygame.init()
#Création de la fenetre graphique
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation0")
fenetre.fill(NOIR)
#Boucle infinie
while True:
#capture et gestion des évènements
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
On veut dessiner un carré rouge dans la fenêtre
# Ce programme dessine un carré rouge dans une
# fenêtre ayant un fonds noir
#importation de librairies
import pygame
#-----------------Constantes-----------------------------------------
LARGEUR = 400
HAUTEUR = 200
COTE_CARRE = 50
NOIR = (0,0,0)
RED = (255,0,0)
#Initialisation de pygame
pygame.init()
#Initialisation de la fenetre graphique
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation1")
fenetre.fill(NOIR)
#Creation du coin supérieur gauche qui repère le
#carré
csg_x = LARGEUR//2
csg_y = HAUTEUR//2
#Dessiner le carré et mettre à jour l'affichage
pygame.draw.rect(fenetre, RED, (csg_x, csg_y, COTE_CARRE, COTE_CARRE))
pygame.display.update()
#pygame.display.flip()
#boucle infinie
while True:
#Gestion des évènements
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
S'assurer d'avoir bien compris le repérage et comment est défini un rectangle
Modifier le programme pour avoir 4 carrés rouges 50x50 aux quatre coins de la fenêtre 400x200
On veut créer l'illusion du déplacement du carré rouge de la droite vers la gauche
Comme le carré est défini par son coin supérieur gauche on va déplacer le coin supérieur gauche régulièrement suivant un "métronome"
Il faut donc déplacer le dessin du carré et la mise à jour de la fenêtre dans la boucle infinie
# Ce programme dessine un carré rouge qui
# se déplace de la gauche vers la droite dans une
# fenêtre ayant un fond noir
#importation des librairies
import pygame
#--------------------Constantes-------------------------------------
LARGEUR = 400
HAUTEUR = 200
COTE_CARRE = 50
DELTA_X = 5
DELTA_Y = 5
NOIR = (0,0,0)
ROUGE = (255,0,0)
#Initialisation de la fenetre graphique
pygame.init()
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation2")
#Création du coin supérieur gauche du carré
csg_x = LARGEUR//2
csg_y = HAUTEUR//2
#Vecteur translation
vecteur = [DELTA_X,0]
#Gestion du rafraichissement de la fenetre
frequence = pygame.time.Clock()
#boucle infinie
while True:
#réception des évènements
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
#mettre a jour les coordonnees du csg
csg_x = csg_x + vecteur[0]
csg_y = csg_y + vecteur[1]
#peindre la fenêtre en noir
fenetre.fill(NOIR)
#dessiner le carré
pygame.draw.rect(fenetre, ROUGE, (csg_x, csg_y, COTE_CARRE, COTE_CARRE))
# Frequence de rafraichisement de la fenêtre
frequence.tick(10)
#mettre a jour la fenêtre
pygame.display.update()
#pygame.display.flip()
On veut insérer une image dans la fenêtre graphique
Télécharger l'image ici
# On charge une image png 99x96
#L'image est repérée par son CSG
import pygame
#------------------------Constantes-----------------------------------
LARGEUR = 800
HAUTEUR = 400
NOIR = (0,0,0)
#Initialisation de pygame
pygame.init()
#Création de la fenetre graphique
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation5")
#chargement de l'image en mémoire vive
image_Balle = pygame.image.load("SoccerBall.png")
#peindre la fenêtre en noir
fenetre.fill(NOIR)
#intégration de l'image à la fenêtre
fenetre.blit(image_Balle,(0,0))
#mettre a jour la fenetre
pygame.display.update()
#boucle infinie
while True:
#receptionnaire evenements
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
La méthode pygame.image.load("SoccerBall.png") permet de charger l'image en mémoire vive (il faut faire attention à la place relative de l'image par rapport au programme Python)
L'instruction fenetre.blit(image_Balle,(0,0)) intégre l'image à la fenêtre
Le coin supérieur gauche de l'image est placé en (0,0) de la fenêtre graphique
Sachant que l'image est de 99x96, placer l'image dans le coin inférieur droit de la fenêtre
On veut créer l'illusion que la balle rebondit dans la fenêtre
# On charge une image png 99x96
# L'image est repérée par son CSG
import pygame
LARGEUR = 800
HAUTEUR = 500
NOIR = (0,0,0)
LARGEUR_IMAGE = 99
HAUTEUR_IMAGE = 96
#csg de l'image
csg = [(LARGEUR-LARGEUR_IMAGE)//2 ,(HAUTEUR-HAUTEUR_IMAGE)//2 ]
#vecteur déplacement de l'image
vecteur = [1,0]
def touche_bords_lateraux()->bool:
"""
renvoie Vrai si l'image touche les bords latéraux de
la fenêtre graphique
"""
return csg[0] > LARGEUR - LARGEUR_IMAGE or csg[0]< 0
def touche_bords_transversaux()->bool:
"""
renvoie Vrai si l'image touche les bords transversaux de
la fenêtre graphique
"""
pass
def mettre_a_jour_csg():
"""
csg = topleft = coin supérieur gauche
"""
if touche_bords_lateraux():
vecteur[0] *= -1
elif touche_bords_transversaux():
pass
csg[0] += vecteur[0]
csg[1] += vecteur[1]
#Initialisation de pygame
pygame.init()
#Initialisation de la fenetre graphique
fenetre = pygame.display.set_mode((LARGEUR,HAUTEUR))
pygame.display.set_caption("Animation5")
fonds = pygame.Surface((LARGEUR,HAUTEUR))
fonds.fill(NOIR)
fenetre.blit(fonds,(0,0))
#Gestion du rafraichissement de la fenetre
frequence = pygame.time.Clock()
#chargement de l'image en mémoire vive
image_balle = pygame.image.load("SoccerBall.png").convert()
#récupérer le rectangle entourant l'image
rect_balle = image_balle.get_rect(topleft = csg)
#intégration de l'image à la fenêtre à la position du csg
fenetre.blit(image_balle,rect_balle)
#boucle infinie
while True:
#receptionnaire evenements
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
#effacer l'image à son ancienne position (csg de rect_balle, deuxième
#argument,en intégrant fonds dans la fenêtre juste sur l'aire de
#rect_balle troisième argument)
#fenetre.blit(fonds,rect_balle,rect_balle)
#mise à jour du csg de l'image
mettre_a_jour_csg()
#mettre à jour le rectangle entourant l'image
rect_balle.update(csg[0],csg[1],LARGEUR_IMAGE,HAUTEUR_IMAGE)
#intégration de l'image à la fenêtre à la position du nouveau csg
fenetre.blit(image_balle,rect_balle)
#mettre a jour la fenetre
pygame.display.update(rect_balle)
# Frequence de rafraichisement de la fenêtre
frequence.tick(60)
Pour remédier à ce problème décommentez l'instruction servant à effacer l'ancienne "image".
Que signifie fenetre.blit(fonds,rect_balle,rect_balle) ?
En programmation on évite d'écrire plusieurs fois la même chose, on dit que l'on factorise le code
Dans le jeu Asteroides, le vaisseau spatial, les asteroides et les missiles sont à peu près les mêmes "objets"
Autant donc définir une classe "commune" que l'on pourrait appeler Animation , car après tout ces objets sont avant tout des images "intégrées" à la fenêtre graphique à un endroit qui évolue avec le temps
Ensuite les classes Vaisseau, Asteroide, Missile seront des classes dérivées ou enfants de la classe Animation
On dit aussi que les classes Vaisseau, Asteroide, Missile héritent de la classe Animation
class Animation:
def __init__(self, image, position:tuple, vitesse:tuple):
#image est de type Surface
self.image = image
#rayon du cercle circonscrit à l'image
self.rayon = self.image.get_width()/2
#position du csg de l'image dans le repère de la fenêtre
self.position = Vector2(position)
#rectangle (de type Rect) entourant l'image
self.rectangle = pygame.Rect(self.position.x,self.position.y,self.rayon,self.rayon)
#centre de l'image
self.centre = self.position + Vector2(self.rayon)
#vitesse de déplacement de l'Animation
self.vitesse = Vector2(vitesse)
def deplacer(self):
self.rectangle.move_ip(self.vitesse.x,self.vitesse.y)
#mise à jour de la position
self.position = self.rectangle.topleft
def dessiner(self,fenetre,aire=None):
fenetre.blit(self.image, self.position,aire)
def entrer_en_collision_avec(self, other):
distance = self.centre.distance_to(other.centre)
return distance < self.rayon + other.rayon