¿Cuál es la mejor manera de estructurar una aplicación Tkinter? [cerrado]

Resuelto Chris Aung asked hace 11 años • 9 respuestas

La siguiente es la estructura general de mi programa típico Python Tkinter.

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()

funAy abrirá otra ventana con widgets cuando el usuario haga clic en el botón 1, 2, 3 funB.funCToplevel

Me pregunto si esta es la forma correcta de escribir un programa Python Tkinter. Claro, funcionará incluso si escribo de esta manera, pero ¿es la mejor manera? Suena estúpido, pero cuando veo el código que otras personas escribieron, su código no está desordenado con un montón de funciones y, en su mayoría, tienen clases.

¿Existe alguna estructura específica que debamos seguir como buena práctica? ¿Cómo debo planificar antes de empezar a escribir un programa Python?

Sé que no existen las mejores prácticas en programación y tampoco las estoy pidiendo. Solo quiero algunos consejos y explicaciones que me mantengan en la dirección correcta mientras estoy aprendiendo Python por mi cuenta.

Chris Aung avatar Jul 04 '13 16:07 Chris Aung
Aceptado

Defiendo un enfoque orientado a objetos. Esta es la plantilla con la que empiezo:

# 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()

Las cosas importantes a tener en cuenta son:

  • No uso una importación con comodines. Importo el paquete como "tk", lo que requiere que anteponga a todos los comandos el prefijo tk.. Esto evita la contaminación global del espacio de nombres y además hace que el código sea completamente obvio cuando se utilizan clases Tkinter, clases ttk o algunas propias.

  • La aplicación principal es una clase . Esto le brinda un espacio de nombres privado para todas sus devoluciones de llamadas y funciones privadas y, en general, facilita la organización de su código. En un estilo procedimental tienes que codificar de arriba hacia abajo, definiendo funciones antes de usarlas, etc. Con este método no lo haces, ya que en realidad no creas la ventana principal hasta el último paso. Prefiero heredar tk.Framesimplemente porque normalmente comienzo creando un marco, pero de ninguna manera es necesario.

Si su aplicación tiene ventanas de nivel superior adicionales, le recomiendo que cada una de ellas sea una clase separada, heredando de tk.Toplevel. Esto le brinda las mismas ventajas mencionadas anteriormente: las ventanas son atómicas, tienen su propio espacio de nombres y el código está bien organizado. Además, facilita colocar cada uno en su propio módulo una vez que el código comienza a crecer.

Finalmente, es posible que desees considerar el uso de clases para cada parte importante de tu interfaz. Por ejemplo, si estás creando una aplicación con una barra de herramientas, un panel de navegación, una barra de estado y un área principal, puedes crear cada una de esas clases. Esto hace que su código principal sea bastante pequeño y fácil de entender:

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)

Dado que todas esas instancias comparten un padre común, el padre efectivamente se convierte en la parte "controladora" de una arquitectura modelo-vista-controlador. Entonces, por ejemplo, la ventana principal podría colocar algo en la barra de estado llamando a self.parent.statusbar.set("Hello, world"). Esto le permite definir una interfaz simple entre los componentes, lo que ayuda a mantener el acoplamiento al mínimo.

Bryan Oakley avatar Jul 04 '2013 12:07 Bryan Oakley

Poner cada una de sus ventanas de nivel superior en su propia clase separada le brinda la reutilización del código y una mejor organización del mismo. Todos los botones y métodos relevantes que estén presentes en la ventana deben definirse dentro de esta clase. Aquí hay un ejemplo (tomado 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()

Ver también:

  • hola mundo simple de tkinter docs
  • Código de ejemplo de Tkinter para múltiples ventanas, ¿por qué los botones no se cargan correctamente?
  • Tkinter: Cómo mostrar/ocultar una ventana

Espero que ayude.

alecxe avatar Jul 04 '2013 09:07 alecxe

Ésta no es una mala estructura; funcionará bien. Sin embargo, es necesario tener funciones en una función para ejecutar comandos cuando alguien hace clic en un botón o algo así.

Entonces, lo que podría hacer es escribir clases para estos y luego tener métodos en la clase que manejen comandos para hacer clic en los botones y demás.

He aquí un ejemplo:

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()

Por lo general, los programas tk con múltiples ventanas son múltiples clases grandes y en __init__todas se crean todas las entradas, etiquetas, etc. y luego cada método es manejar eventos de clic de botón.

Realmente no existe una manera correcta de hacerlo, lo que sea que funcione para usted y haga el trabajo siempre que sea legible y pueda explicarlo fácilmente porque si no puede explicar fácilmente su programa, probablemente haya una mejor manera de hacerlo. .

Take a look at Thinking in Tkinter.

Serial avatar Jul 04 '2013 09:07 Serial