216 votes

La meilleure façon de structurer une application tkinter ?

Voici la structure générale de mon programme python tkinter typique.

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff

root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB y funC fera apparaître un autre Toplevel Fenêtres avec widgets lorsque l'utilisateur clique sur le bouton 1, 2, 3.

Je me demande si c'est la bonne façon d'écrire un programme python tkinter ? Bien sûr, cela fonctionnera même si j'écris de cette façon, mais est-ce la meilleure façon ? Cela semble stupide mais quand je vois les codes écrits par d'autres personnes, leur code n'est pas embrouillé par un tas de fonctions et la plupart ont des classes.

Y a-t-il une structure spécifique que nous devrions suivre comme bonne pratique ? Comment dois-je planifier avant de commencer à écrire un programme python ?

Je sais que les meilleures pratiques en matière de programmation n'existent pas et je ne les demande pas non plus. Je veux juste des conseils et des explications pour rester dans la bonne direction alors que j'apprends Python tout seul.

415voto

Bryan Oakley Points 63365

Je préconise une approche orientée objet. C'est le modèle avec lequel je commence :

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Les éléments importants à noter sont :

  • Je n'utilise pas d'importation joker. J'importe le paquet en tant que "tk", ce qui nécessite de préfixer toutes les commandes par tk. . Cela évite la pollution de l'espace de nom global, et rend le code complètement évident quand vous utilisez des classes Tkinter, des classes ttk, ou certaines de vos propres classes.

  • L'application principale est une classe . Vous disposez ainsi d'un espace de nom privé pour tous vos callbacks et fonctions privées, ce qui facilite l'organisation de votre code. Dans un style procédural, vous devez coder de haut en bas, en définissant les fonctions avant de les utiliser, etc. Avec cette méthode, ce n'est pas le cas puisque la fenêtre principale n'est créée qu'à la toute dernière étape. Je préfère hériter de tk.Frame juste parce que je commence généralement par créer un cadre, mais ce n'est en aucun cas nécessaire.

Si votre application comporte des fenêtres de niveau supérieur supplémentaires, je vous recommande de créer une classe distincte pour chacune d'entre elles, en héritant de la classe tk.Toplevel . Cela vous donne tous les mêmes avantages que ceux mentionnés ci-dessus : les fenêtres sont atomiques, elles ont leur propre espace de noms et le code est bien organisé. De plus, il est facile de placer chaque fenêtre dans son propre module lorsque le code commence à être volumineux.

Enfin, vous pouvez envisager d'utiliser des classes pour chaque partie importante de votre interface. Par exemple, si vous créez une application avec une barre d'outils, un volet de navigation, une barre d'état et une zone principale, vous pouvez créer des classes pour chacun de ces éléments. Cela rend votre code principal assez petit et facile à comprendre :

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

        self.statusbar.pack(side="bottom", fill="x")
        self.toolbar.pack(side="top", fill="x")
        self.navbar.pack(side="left", fill="y")
        self.main.pack(side="right", fill="both", expand=True)

Comme toutes ces instances partagent un parent commun, le parent devient effectivement la partie "contrôleur" d'une architecture modèle-vue-contrôleur. Ainsi, par exemple, la fenêtre principale peut placer quelque chose sur la barre d'état en appelant self.parent.statusbar.set("Hello, world") . Cela vous permet de définir une interface simple entre les composants, ce qui contribue à réduire le couplage au minimum.

56voto

alecxe Points 50783

Le fait de placer chacune de vos fenêtres de premier niveau dans une classe distincte vous permet de réutiliser le code et de mieux l'organiser. Tous les boutons et méthodes pertinents présents dans la fenêtre doivent être définis dans cette classe. Voici un exemple (tiré de aquí ):

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Voir aussi :

J'espère que cela vous aidera.

9voto

Serial Points 5378

Cette structure n'est pas mauvaise ; elle fonctionnera très bien. Cependant, vous devez avoir des fonctions dans une fonction pour effectuer des commandes lorsque quelqu'un clique sur un bouton ou quelque chose du genre

Ce que vous pourriez faire, c'est d'écrire des classes pour ces derniers, puis d'avoir des méthodes dans la classe qui gèrent les commandes pour les clics de bouton et autres.

Voici un exemple :

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2

class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Window1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Habituellement, les programmes tk avec de multiples fenêtres sont de grandes classes multiples et dans les __init__ toutes les entrées, les étiquettes, etc. sont créées, puis chaque méthode doit gérer les événements de clics sur les boutons.

Il n'y a pas vraiment de bonne façon de faire, tout ce qui vous convient et qui vous permet de faire votre travail, tant que c'est lisible et que vous pouvez l'expliquer facilement. En effet, si vous ne pouvez pas expliquer facilement votre programme, il existe probablement une meilleure façon de le faire.

Jetez un coup d'œil à Penser en Tkinter .

2voto

Il Divin Codino Points 1708

OOP devrait être l'approche et frame devrait être un variable de classe au lieu de variable d'instance .

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

enter image description here

Référence : http://www.python-course.eu/tkinter_buttons.php

1voto

Gab ПК Points 13

Ma façon préférée de le faire est comme la réponse de Bryan Oakley. Voici un exemple, réalisé par Sentdex sur Youtube, allez voir sa playlist "GUIs with Tkinter".

Je pense qu'il est vraiment pertinent de le mettre ici parce que c'est un excellent exemple pour l'OP, et donc il répond également à cette réponse qui a été soulevée par 35 personnes et n'a pas été répondu ;

@Bryan Oakley connaissez-vous de bons exemples de codes sur Internet que je puisse étudier leur structure ? - Chris Aung 5 Jul 13 at 8:35

import tkinter as tk

LARGE_FONT= ("Verdana", 12)

class SeaofBTCapp(tk.Tk):
    """
    tkinter example app with OOP
    """

    def __init__(self, *args, **kwargs):

        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand = True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for frame_class in (StartPage,PageOne, PageTwo):

            frame = frame_class(container, self)

            self.frames[frame_class] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        """
        Put specific frame on top
        """

        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):
    """
    Starting frame for app
    """

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent,bg='grey')
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        button_page1 = tk.Button(self, text = 'Visit Page 1', command= lambda: controller.show_frame(PageOne))
        button_page1.pack()

        button_page2 = tk.Button(self, text = 'Visit Page 2', command= lambda: controller.show_frame(PageTwo))
        button_page2.pack()

class PageOne(tk.Frame):
    """
    First page of program
    """

    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent,bg='light blue')
        label = tk.Label(self, text="Page one", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
        button_home.pack()

        button_home = tk.Button(self, text = 'Go to page2', command= lambda: controller.show_frame(PageTwo))
        button_home.pack()

class PageTwo(tk.Frame):
    """
    First page of program
    """

    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent,bg='light green')
        label = tk.Label(self, text="Page two", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
        button_home.pack()

        button_home = tk.Button(self, text = 'Go to page1', command= lambda: controller.show_frame(PageOne))
        button_home.pack()

app = SeaofBTCapp()
app.mainloop()

Trouvez le code ici aussi : [https://pythonprogramming.net/change-show-new-frame-tkinter/]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X