Optimiser mon code d'interface tkinter

faire une boucle pour afficher des boutons avec tkinter

a marqué ce sujet comme résolu.

Bonjour, pour un projet d’école je dois coder un tarot. A un moment j’aimerai faire une def pour afficher à partir d’une liste de str, les cartes correspondantes. On aurait donc à cliquer sur la carte et la def devrait return le rang de cette carte dans la liste d’origine.

sachant que la listA est sous cette forme : listA = [(’1',’H'), (’2',’H'), (’K',’H')] => ce qui correspond à (as de coeur), (2 de coeur), (roi de coeur).

def CardsInterface(listA, txtButton, txtTitle):

root = Tk()
root.title(txtTitle)

Label(root, text = txtButton, font =('Verdana', 10)).pack()

for i in listA:

    if i == ('1','H'):
        def test():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("('1', 'H')")
        photo1H = PhotoImage(file ="COEUR\\1_coeur.png", height=300, width=198).subsample(3)
        button1H = Button(root, image = photo1H, command=test)
        button1H.pack(side = LEFT)
        
     if i == ('2','H'):
        def test():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("('2', 'H')")
        photo2H = PhotoImage(file ="COEUR\\2_coeur.png", height=300, width=198).subsample(3)
        button2H = Button(root, image = photo2H, command=test)
        button2H.pack(side = LEFT)

    #if pour chacune des 78 cartes !!!

root.mainloop()

with open("CardChoosen.txt", "r") as f:
    otherVariable = f.read()

print(otherVariable)

for i in listA:
    if str(i) == otherVariable: return listA.index(i)

La définition fonctionne mais comme vous pouvez le voir c’est vraiment pas optimisé car:

  • il faut que je fasse un if pour les 78 cartes
  • pour obtenir la valeur de la carte sur laquelle j’ai cliqué je dois l’écrire sur un fichier texte puis la récupérée et enfin trouver son rang dans la liste d’origine

J’aimerai donc savoir si quelqu’un saurait comment simplifier ce code ? Au moins pour que je n’ai pas à faire 8 lignes pour chaque carte ?

Bonjour,

Au lieu de répéter le code que tu fais tu dois l’associer par rapport à la carte courante.

    if i == ('1','H'):
        def test():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("('1', 'H')")
        photo1H = PhotoImage(file ="COEUR\\1_coeur.png", height=300, width=198).subsample(3)
        button1H = Button(root, image = photo1H, command=test)
        button1H.pack(side = LEFT)

Dans l’idée tu transformes le code en quelque chose comme cela :

        shape = get_shape(i[1]) // associer le signe de la carte à la chaine correspondante
        def test():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("(i[0], i[1])")
        photo = PhotoImage(file ="{}\\{}_{}.png".format(shape, i[0], shape), height=300, width=198).subsample(3)
        button = Button(root, image = photo, command=test)
        button.pack(side = LEFT)

En gros tu dois juste écrire une fonction qui à une valeur et un signe va avoir le comportement que tu souhaites (tu as un début d’idée dans le deuxième extrait de code). Puis appeler cette fonction pour chacune des cartes dans ta boucle for.

+0 -0

Je vous remercie beaucoup pour votre réponse. j’ai essayé ce que vous m’avez conseiller (en espérant avoir bien compris).

Cependant lorsque je fais tourner le code il n’y a plus d’affiché que la dernière carte : (le reste sont des boutons vide)

def CardsInterface(listA, txtButton, txtTitle):

root = Tk()
root.title(txtTitle)

Label(root, text = txtButton, font =('Verdana', 10)).pack()

for i in listA:
    if i[1] == 'H': shape = 'coeur'
    if i[1] == 'D': shape = 'carreau'
    if i[1] == 'C': shape = 'treffles'
    if i[1] == 'S': shape = 'pic'
    
    def test():
        root.destroy()
        with open("CardChoosen.txt", "w") as f:
            f.write("('{}', '{}')".format(i[0], i[1]))
    photo = PhotoImage(file ="{}\\{}_{}.png".format(shape, i[0], shape), height=300, width=198).subsample(3)
    button = Button(root, image = photo, command=test)
    button.pack(side = LEFT)

root.mainloop()

with open("CardChoosen.txt", "r") as f:
    otherVariable = f.read()

print(otherVariable)

for i in listA:
    if str(i) == otherVariable: return listA.index(i)

#exemple

deck = [(’1', 'H’), (’2', 'H’), (’3', 'H’), (’7', 'H’), (’8', 'H’)] print(CardsInterface(deck, 'test’, 'test’))

Il y a, je pense un problème de closure dans ton code, même en cliquant sur les boutons (sans aucune photo) c’est systématiquement le même print qui est affiché.

Tu peux voir plus de détails sur ce cours : https://zestedesavoir.com/tutoriels/3163/variables-scopes-et-closures-en-python/

Tu peux fixer les variables en les passant en paramètres d’une fonction (qui renverra la fonction test()) pour pallier à ce problème (voir code ci-dessous, attention à utiliser des noms de variables plus explicites).

from tkinter import *
root = Tk()
root.title("title")

Label(root, text = "txtButton", font =('Verdana', 10)).pack()

listA = [(1, "H"), (2, "H"), (3, "H"), (7, "H"), (8, "H")]

for i in listA:
    if i[1] == 'H': shape = 'coeur'
    if i[1] == 'D': shape = 'carreau'
    if i[1] == 'C': shape = 'treffles'
    if i[1] == 'S': shape = 'pic'

    def test(a, b):
        def test2():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("('{}', '{}')".format(a, b))
        return test2
    button = Button(root, command=test(i[0], i[1]))
    button.pack(side = LEFT)
...

À priori le problème des images est dans la même veine.

+0 -0

Sauvegarder les images dans une liste contenue dans le scope englobant semble résoudre le problème pour les images mais je ne sais pas t’expliquer le pourquoi, peut-être une histoire d’image qui est garbage collectée (voir https://stackoverflow.com/questions/22200003/tkinter-button-not-showing-image).


from tkinter import *
root = Tk()
root.title("title")

Label(root, text = "txtButton", font =('Verdana', 10)).pack()

listA = [(1, "H"), (2, "H"), (3, "H"), (7, "H"), (8, "H")]

images = []
for i in listA:
    if i[1] == 'H': shape = 'coeur'
    if i[1] == 'D': shape = 'carreau'
    if i[1] == 'C': shape = 'treffles'
    if i[1] == 'S': shape = 'pic'

    def test(a, b):
        def test2():
            root.destroy()
            with open("CardChoosen.txt", "w") as f:
                f.write("('{}', '{}')".format(a, b))
        return test2
    photo = PhotoImage(...)
    images.append(photo)
    button = Button(root, image = photo, command=test(i[0], i[1]))
    button.pack(side = LEFT)
...

P.S non lié: de manière générale, pour rendre ton code plus propre peut-être est-il intéressant d’abstraire ta structure de carte dans une classe. Et le code du type :

    if i[1] == 'H': shape = 'coeur'
    if i[1] == 'D': shape = 'carreau'
    if i[1] == 'C': shape = 'treffles'
    if i[1] == 'S': shape = 'pic'

dans des fonctions (ex: get_shape_from_shape_abbreviation).

+0 -0

Bonjour et bravo !

Je n’y suis pas pour grand chose ;)

Pardon pour le message qui suit qui est un peu indigeste.

Quelques remarques : 1. C’est cool d’avoir mis le code sur git mais le but principal de l’outil est de permettre de suivre les changements des fichiers, en mettant des archives dessus ce n’est pas possible (il aurait fallu jamais compresser en zip et simplement ajouter l’ensemble des fichiers au fur et à mesure).

  1. C’est bien d’avoir essayé de documenté un peu le code, c’est en faisant qu’on apprend !

  2. En python c’est souvent le style de pep8 (https://www.python.org/dev/peps/pep-0008/) qui est utilisé pour la présentation du code. C’est intéressant de le suivre pour avoir un style de code uniforme lorsqu’on travaille à plusieurs (ce qui était le cas ici !). Il est tout à fait possible de configurer son éditeur de code pour avoir du linting automatique par rapport aux règles de pep8. En particulier snake_case plutôt que camelCase.

  3. Au niveau du code en lui même : il y a beaucoup d’endroits où vous pourriez simplifier des éléments de code :

Voilà quelques idées (dans la dernière version du code), je n’ai fait que regardé rapidement (mais c’est applicable à pleins d’autres endroits).

Dans code_tarot_definition.py De manière générale il y a beaucoup de code dupliqué, peut-être qu’il aurait été intéressant de créer des variables globales pour les différentes cartes du jeu (au moins pour les listes sur lesquelles vous itérez systématiquement), à savoir : color = ["D","H","C","S"], high_values = ["1","2","3","4","5","6","7","8","9","10","J","C","Q","K"] et [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21].

Ainsi on peut transformer :

1
def cardDeck():
    """create and shuffle a deck of cards

    Returns:
        list: list of cards
    """
    color = ["D","H","C","S"]
    high_values = ["1","2","3","4","5","6","7","8","9","10","J","C","Q","K"]
    deck = [(x,y) for y in color for x in high_values]
    for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]:
        deck.append((str(x),"A"))
    deck.append(('fool', 'A'))
    random.shuffle(deck)
    return deck

en

COLORS = ["D", "H", "C", "S"]
HIGH_VALUES = [str(x) for x in range(1, 11] + ["J","C","Q","K"]
ATOUTS = [str(x) for x in range(1, 22)] + ["fool"]

def cardDeck():
    deck = [(x,y) for y in COLORS for x in HIGH_VALUES] + [(x, "A") for x in ATOUTS]
    random.shuffle(deck)
    return deck

puis en profiter pour réutiliser COLORS, HIGH_VALUES et ATOUTS autant que possible.

De la même manière, il faut essayer de ne pas se répéter (https://fr.wikipedia.org/wiki/Ne_vous_r%C3%A9p%C3%A9tez_pas)

Vous pouvez modifier :

2
def distribution(listA, listP1, listP2, listP3, listP4, Deck):
    if listA == listP1:
        for x in [0, 1, 2]:
            for y in [0, 12, 24, 36, 48, 60]:
                listA.append(Deck[x + y])
    if listA == listP2:
        for x in [3, 4, 5]:
            for y in [0, 12, 24, 36, 48, 60]:
                listA.append(Deck[x + y])
    if listA == listP3:
        for x in [6, 7, 8]:
            for y in [0, 12, 24, 36, 48, 60]:
                listA.append(Deck[x + y])
    if listA == listP4:
        for x in [9, 10, 11]:
            for y in [0, 12, 24, 36, 48, 60]:
                listA.append(Deck[x + y])
    return listA

en

def distribution(listA, listP1, listP2, listP3, listP4, deck):
    initial_x = [listP1, listP2, listP3, listP4].index(listA) * 3
    for x in [initial_x, initial_x + 1, initial_x + 2]:
        for y in range(0, 5 * 12 + 1, 12):
            listA.append(deck[x + y])
    return listA

De manière générale, pas besoin de passer en entrée de votre fonction la liste où vous voulez ajouter des éléments. C’est parfois plus simple / pratique de la créer sur le champ.

(On suppose que player_id = 0 / 1 / 2 / 3)

def distribution(player_id, deck):
    initial_x = player_id * 3
    listA = []
    for x in [initial_x, initial_x + 1, initial_x + 2]:
        for y in range(0, 5 * 12 + 1, 12):
            listA.append(deck[x + y])
    return listA

etc.

Puis plutôt que :

    p1 = card_sorted(distribution(p1, p1, p2, p3, p4, finalDeck))
    p2 = card_sorted(distribution(p2, p1, p2, p3, p4, finalDeck))
    p3 = card_sorted(distribution(p3, p1, p2, p3, p4, finalDeck))
    p4 = card_sorted(distribution(p4, p1, p2, p3, p4, finalDeck))

on peut faire

p1, p2, p3, p4 = [distribution(player_id, deck) for player_id in range(0, 4)]
3

De manière générale, utiliser les compréhensions lorsque c’est possible :

    nb_sort = []
    for i in listB:
        nb_sort_i = transformation[i]
        nb_sort.append(nb_sort_i)
    nb_sort.sort()

devient

    nb_sort = [transformation[i] for i in listB]
    nb_sort.sort()

Un article de ZdS qui donne explique quelques "directions" lorsqu’on programme en python https://zestedesavoir.com/articles/1079/les-secrets-dun-code-pythonique/

Bonne continuation et bon apprentissage ! :)

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte