My code basically does this:
Which is clearly not what I want to try. For further clarification I would like my window to look similar to this:
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()
I am clearly not getting something right. The whole reason I rewrote the program is so that I could gain a greater understanding/confidence with object oriented code. I am hoping that I can get better insight with this, so any help would be amazing.
There are are several problems going on with your code.
Perhaps the biggest problem is that Encoding
inherits from tk.Tk
, MainApplication
inherits from tk.Frame
and Encoding
(making it both a root window and a frame), and then MainApplication
creates an instance of Encoding
. Plus, you explicitly create another instance of tk.Tk()
, giving you two root windows. That all needs to be untangled.
Inheritance create a "is a" relationship. By having MainApplication
inherit from Encoding
you are saying that MainApplication
is a Encoding
object. That is not the case in your code - an Encoding
object represents only a small part of the application. For that you want composition, not inheritance, ie: MainApplication
has a Encoding
object.
So, the first step is to remove Encoding
from the list of classes that MainApplication
inherits from.
Another thing that can probably be removed is self.encoding_frame
. I see no reason to have it since MainApplication
itself is a frame. Instead, have MainApplication
inherit from ttk.LabelFrame
rather than tk.Frame
.
The final thing is that since MainApplication
creates Encoding
, it should be responsible for calling grid
or pack
on the instance of Encoding
.
Altogether, MainApplication
can be pared down to this:
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")
That's not 100% complete, but it's a good place to start. Based on your image I'm guessing you'll have other classes for other parts of the main application -- the message widget, the key widgets, and the transcription window.
For Encoding
, much of the same advice applies. Since it's only part of the application, it shouldn't inherit from tk.Tk
. Instead, you can inherit from ttk.Frame
and then remove self.encoding_frame
since the Encoding
object itself is already a frame.
With those changes, Encoding
should look something like the following. Notice how the radiobuttons have self
as their parent. If you're creating proper objects, all widgets inside the class need to be a child of the class itself or one of its descendants. A class like this should never put anything in parent
except itself.
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)
Finally, since MainApplication
is now a frame -- instead of inheriting from Encoding
which inherits from tk.Tk
-- the block of code that creates an instance of MainApplication
needs to be responsible for calling pack
or grid
. Since MainApplication
is the only widget directly inside of the root window, pack
is the best choice since you don't have to remember to configure row and column weights to get proper behavior when the window is resized.
Also, I recommend creating root
in the same block rather than at the very start of the program.
Your bottom block of code should look like this:
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
app.pack(fill="both", expand=True)
root.mainloop()