Une proposition de réponse :
# -*- coding: utf-8 -*-
from tkinter import Canvas, Tk
import random
def _create_circle(self, x, y, r, **kwargs):
"""Fonction pour créer les cercles à partir du centre
(à but de simplicité)
:param x: Le centre x du cercle à créer
:param y: Le centre y du cercle à créer
:param r: Le rayon du cercle à créer
:type x: int
:type y: int
:type r: int
:return: Le return de la fonction Tkinter create_oval()
:rtype: int
"""
return self.create_oval(x - r, y - r, x + r, y + r, **kwargs)
def onCircleRotationClick(x):
"""Fonction de gestionnaire d'évenement clic sur cercle de rotation
:param x: Variable de l'évenement du clic
"""
global animating #Variable globale pour qu'elle soit modifiable par d'autres fonctions
if not animating:#On ne peut faire des rotations que si l'animation est terminée
offset = 120 #Variable de zone de recherche
#print('Got object click', x.x, x.y) DEBUG
#
global closest #ID du cercle de rotation à l'origine du clic
global closestes #ID des cercles et des textes liés au cercle de rotation
#Pour que les variables soient accessibles par d'autres fonctions
closest = (x.widget.find_closest(x.x, x.y))#Recherche du cercle de rotation à l'origine du clic
closestes = (x.widget.find_enclosed(x.x - ( offset ), x.y -(offset) , x.x +(offset), x.y +(offset)))#Recherche des cercles et des textes proches du cercle de rotation en utilisant un rectangle pour définir la zone de recherche
anim_rotation_cercle()
def anim_rotation_cercle(movement = 10):
"""
Fonction qui gère l'animation de la rotation du cercle
:param movement: Valeur du mouvement déjà effectué (Par défaut 10 si aucun mouvement effectué)
:type movement: int
"""
global animating
animating = True
ecart_cercle = 150 #Constante de distance de déplacement des cercles
i=1
rowFinal = 3 #Variable pour définir la ligne du cercle en haut à gauche (référence pour la rotation)
columnFinal = 3 #Variable pour définir la colonne du cercle en haut à gauche (référence pour la rotation)
for obj in closestes:#On vérifie tous les objets trouvés par la fonction find_enclosed()
#Déterminer les positions d'objets
if not(obj in cercles_rotation[0] or obj in cercles_rotation[1]):
if not findRow(cercles, obj) == None: #Si l'obj du Canvas est bien un cercle
#Objectif : trouver le premier cercle en haut à gauche afin de trouver les trois autres
row = findRow(cercles,obj)
column = cercles[row].index(obj)
if row == rowFinal and column < columnFinal:
columnFinal = column
if row < rowFinal:
rowFinal = row
columnFinal = column
rotate_cercles(rowFinal, columnFinal)
movement = movement + 10
if(movement <= ecart_cercle):#Si l'animation n'est pas finie
canvas.after(16, lambda move=movement: anim_rotation_cercle(move)) #Toutes les 16 milisecondes, on bouge l'objet d'une valeur donnée
else:
update_array_rotate(rowFinal, columnFinal)#Une fois l'animation terminée, on met à jour le tableau des cercles et des textes
canvas.after(50, setAnimatingToFalse)#Délai pour éviter les bugs de rotation
def rotate_cercles(firstRow, firstColumn):
"""Fonction qui déclenche la rotation graphique des cercles
:param firstRow: Numéro de la ligne du cercle en haut à gauche
:param firstColumn: Numéro de la colonne du cercle en haut à gauche
:type firstRow: int
:type firstColumn: int
"""
#Toutes les objets sont déplacés d'une valeur de 10 pour une animation fluide
canvas.move(cercles[firstRow][firstColumn], 10 ,0) #Cercle Gauche/Haut
canvas.move(textes_cercles[firstRow][firstColumn], 10 ,0) #CercleTexte Gauche/Haut
canvas.move(cercles[firstRow][firstColumn + 1], 0, 10) #Cercle Droit/Haut
canvas.move(textes_cercles[firstRow][firstColumn + 1], 0, 10) #CercleTexte Droit/Haut
canvas.move(cercles[firstRow + 1][firstColumn], 0, -10) #Cercle Gauche/Bas
canvas.move(textes_cercles[firstRow + 1][firstColumn], 0, -10) #CercleTexte Gauche/Bas
canvas.move(cercles[firstRow + 1][firstColumn + 1], -10 ,0) #Cercle Droite/Bas
canvas.move(textes_cercles[firstRow + 1][firstColumn + 1], -10 ,0) #CercleTexte Droite/Bas
def update_array_rotate(row, column):
"""Fonction pour la rotation des valeurs dans le tableau de variable
:param row: Numéro de la ligne du cercle en haut en gauche
:param column: Numéro de la colonne du cercle en haut en gauche
:type row: int
:type column: int
"""
#On effectue la rotation dans le tableau à deux dimensions
cercles[row][column], cercles[row][column+1], cercles[row+1][column], cercles[row+1][column +1] = cercles[row + 1][column], cercles[row][column], cercles[row +1][column + 1], cercles[row][column+1]
# Haut Gauche Haut Droit Bas Gauche Bas Droit
textes_cercles[row][column], textes_cercles[row][column+1], textes_cercles[row+1][column], textes_cercles[row+1][column +1] = textes_cercles[row + 1][column], textes_cercles[row][column], textes_cercles[row +1][column + 1], textes_cercles[row][column+1]
testwin()#On regarde si l'utilisateur a gagné
def findRow(tableau, entree):
"""Fonction pour trouver le numéro de colonne dans un tableau à deux dimensions
:param tableau:Le tableau à deux dimensions dans lequel chercher le numéro de colonne
:param entree: L'ID de l'objet dont il faut trouver le numéro de colonne
:type tableau: int
:type entree: int
:return: Le numéro de la colonne trouvée correspondant à l'objet
:rtype: int
"""
rowNumber = None
for row in tableau:
if(entree in row):
rowNumber = tableau.index(row)
break
return rowNumber
def testwin():
"""Fonction pour tester si l'utilisateur a gagné
La fonction crée un texte si l'utilisateur a gagné
"""
if cercles == [[1,3,5],[7,9,11], [13,15,17]]:
print("WIN")
canvas.create_text(600/2,40, text="Vous avez gagné !", font=("Segoe UI", 35), anchor="center")
def setAnimatingToFalse():
"""Fonction pour débloquer la rotation de cercles (pour éviter les bugs)"""
global animating
animating = False
Canvas.create_circle = _create_circle
# Création fenêtre
fenetre = Tk()
win_width = 600
win_height = 600
# Création d'un espace de dessin
canvas = Canvas(fenetre, width=win_width, height=win_height, background="white")
# Ajout du canvas à la fenêtre
lignesGrilles = []
cercles = [[0,0,0],
[0,0,0],
[0,0,0]] # Cercles conteneurs chiffre / En 2D, 3 cercles x 3 cercles = 9 cercles
textes_cercles = [[0,0,0],
[0,0,0],
[0,0,0]] # Textes associés aux cercles / En 2D, 3 textes x 3 textes = 9 textes
cercles_rotation = [[], []] # Cercles de rotations, 4 cercles = 2 cercles * 2 cercles
couleur_cercles = ["blue","green", "red"] #Couleurs des cercles
debug = False #DEBUG
closest = None #Variable qui contient l'ID du dernier cercle de rotation cliqué
closestes = None #Variable qui contient les ID des cercles et des textes autour du cercle de rotation
animating = False #Variable globale qui indique si une animation est en cours
randomPositions = [0,1,2,3,4,5,6,7,8] #Variables de placement de cercles et des textes de cercles
random.shuffle(randomPositions)#Mélange des positions
# Création d'une grille ( à but de déboguage )
if debug:
for i in range(2):
lignesGrilles.append([])
for ii in range(3):
if i == 0:
lignesGrilles[i].append(canvas.create_line(600 * (ii + 1) / 4, 0, 600 * (ii + 1) / 4, 600))
else:
lignesGrilles[i].append(canvas.create_line(0, 600 * (ii + 1) / 4, 600, 600 * (ii + 1) / 4))
# Création des cercles et des textes
actualCirclePosition = 0 #Itérateur pour garder en mémoire le chiffre associé au cercle
for i in range(3):
for ii in range(3):
ligne = int((randomPositions[actualCirclePosition] - randomPositions[actualCirclePosition] % 3) / 3) #EN comptant à partir de zero
#La position moins le reste de la division par 3 divisé par 3 = ligne
colonne = (randomPositions[actualCirclePosition] % 3)
cercles[ligne][colonne] = (canvas.create_circle((colonne + 1) / 4 * win_width, (ligne + 1) / 4 * win_height, 30, outline=couleur_cercles[i]))
textes_cercles[ligne][colonne] = (canvas.create_text((colonne + 1) / 4 * win_width, (ligne + 1) / 4 * win_height, text=str((ii + 1) + (3*i) ), font=("Helvetica", 26), fill=couleur_cercles[i]))
actualCirclePosition = actualCirclePosition + 1
#Création des cercles de rotation
for l in range(2):
for c in range(2):
cercles_rotation[l].append(canvas.create_circle( (3 + (2*c)) /8*win_width, (3+(2*l)) /8*win_height, 15, fill="deep sky blue", outline=""))
#Ajout d'un listener sur le clic d'un cercle de rotation
canvas.tag_bind(cercles_rotation[l][c], "", onCircleRotationClick)
canvas.pack()
# Demarrage de la boucle
fenetre.mainloop()