Mi código básicamente hace esto:

my problem

Que claramente no es lo que quiero probar. Para mayor aclaración, me gustaría que mi ventana se vea similar a esto:

an ideal solution

from tkinter import *
import tkinter as tk
from tkinter import ttk

root = tk.Tk()

class Encoding(tk.Tk):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.mode = StringVar()
##      If I remove the next line it breaks it entirely.
        self.encoding_frame = ttk.Frame(parent)
        self.encrypt = ttk.Radiobutton(self.encoding_frame, text='Encrypt', variable=self.mode, value='encrypt')
        self.decrypt = ttk.Radiobutton(self.encoding_frame, text='Decrypt', variable=self.mode, value='decrypt')
        self.encrypt.grid(column=0, row=0, ipadx=2, sticky=W)
        self.decrypt.grid(column=0, row=1, ipadx=2, sticky=W)
        self.encoding_frame.grid(column=0, columnspan=3, row=2, sticky=S)


class MainApplication(tk.Frame, Encoding):
    # Create a main frame here.
    # Would like frames to be nested within this frame. This seems redundant but nesting with a main
    # frame allows for consistent themes, and gives additional control of layout between subframes.
    # Inheritance is confusing.
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.main_frame = ttk.LabelFrame(parent, text="Main Window", width=500, height=500)
        self.main_frame['borderwidth'] = 3
        self.main_frame['relief'] = 'raised'
        self.main_frame.grid(column=0, row=0)
        self.encoding = Encoding(self)
##      I wrote the following line hoping that I could have main_frame become the parent frame.
        self.encoding.encoding_frame = ttk.LabelFrame(self.main_frame)


if __name__ == "__main__":
    app = MainApplication(root)
    root.mainloop()

Claramente no estoy haciendo algo bien. La única razón por la que reescribí el programa es para poder obtener una mayor comprensión / confianza con el código orientado a objetos. Espero poder obtener una mejor comprensión con esto, por lo que cualquier ayuda sería increíble.

1
bluedays 25 oct. 2019 a las 01:28

1 respuesta

La mejor respuesta

Hay varios problemas con su código.

Quizás el mayor problema es que Encoding hereda de tk.Tk, MainApplication hereda de tk.Frame y Encoding (lo que lo convierte en una raíz ventana y un marco), y luego MainApplication crea una instancia de Encoding. Además, crea explícitamente otra instancia de tk.Tk(), lo que le brinda dos ventanas raíz. Todo eso necesita ser desenredado.

La herencia crea una relación "es una" . Al hacer que MainApplication herede de Encoding, estás diciendo que MainApplication es un objeto Encoding. Ese no es el caso en su código: un objeto Encoding representa solo una pequeña parte de la aplicación. Para eso desea composición , no herencia , es decir: MainApplication tiene un objeto Encoding.

Entonces, el primer paso es eliminar Encoding de la lista de clases de las que MainApplication hereda.

Otra cosa que probablemente se puede eliminar es self.encoding_frame. No veo ninguna razón para tenerlo ya que MainApplication en sí mismo es un marco. En su lugar, haga que MainApplication herede de ttk.LabelFrame en lugar de tk.Frame.

Lo último es que, dado que MainApplication crea Encoding, debería ser responsable de llamar a grid o pack en la instancia de Encoding.

En total, MainApplication se puede reducir a esto:

class MainApplication(ttk.LabelFrame):
    def __init__(self, parent, *args, **kwargs):
        ttk.LabelFrame.__init__(self, parent, *args, **kwargs)

        self.configure(text="Main Window")
        self['borderwidth'] = 3
        self['relief'] = 'raised'

        self.encoding = Encoding(self)
        self.encoding.grid(row=0, column=0, sticky="ew")

Eso no está completo al 100%, pero es un buen lugar para comenzar. Según su imagen, supongo que tendrá otras clases para otras partes de la aplicación principal: el widget de mensajes, los widgets clave y la ventana de transcripción.

Para Encoding, se aplican muchos de los mismos consejos. Dado que es solo una parte de la aplicación, no debería heredar de tk.Tk. En su lugar, puede heredar de ttk.Frame y luego eliminar self.encoding_frame ya que el objeto Encoding en sí mismo ya es un marco.

Con esos cambios, Encoding debería tener un aspecto similar al siguiente. Observe cómo los botones de radio tienen self como padre. Si está creando objetos adecuados, todos los widgets dentro de la clase deben ser secundarios de la clase en sí o uno de sus descendientes. Una clase como esta nunca debería poner nada en parent excepto a sí misma.

class Encoding(ttk.Frame):
    def __init__(self, parent, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)

        self.mode = StringVar()
        self.encrypt = ttk.Radiobutton(self, text='Encrypt', variable=self.mode, value='encrypt')
        self.decrypt = ttk.Radiobutton(self, text='Decrypt', variable=self.mode, value='decrypt')

        self.encrypt.grid(column=0, row=0, ipadx=2, sticky=W)
        self.decrypt.grid(column=0, row=1, ipadx=2, sticky=W)

Finalmente, dado que MainApplication ahora es un marco, en lugar de heredar de Encoding que hereda de tk.Tk, el bloque de código que crea una instancia de MainApplication debe ser responsable de llamar a pack o grid. Dado que MainApplication es el único widget directamente dentro de la ventana raíz, pack es la mejor opción ya que no tiene que recordar configurar los pesos de fila y columna para obtener el comportamiento adecuado cuando se cambia el tamaño de la ventana.

Además, recomiendo crear root en el mismo bloque en lugar de al comienzo del programa.

Su bloque de código inferior debería verse así:

if __name__ == "__main__":
    root = tk.Tk()
    app = MainApplication(root)
    app.pack(fill="both", expand=True)
    root.mainloop()
1
Bryan Oakley 25 oct. 2019 a las 02:51