I have just moved my layout in Tkinter from pack to grid. This is going well. I made the mistake of using pack and grid together in my Main function. I fix it and not only using grip. But I realized that I am still using pack inside a class that also builds Tkinter widgets. These are all labels. It appears this is okay and it work but I don't think this a good practice to follow. If I use a grid in the Class will this then embed its own grid inside the grid of the Main? What is the best way to layout the screen when some widgets are created in a class and some in the Main?
So here is the class with the pack still in it....
class StopWatch(Frame):
...
def makeWidgets(self):
""" Make the time labels. """
la = Label(self, textvariable=self.timestr)
la.pack(fill=X, expand=NO, pady=2, padx=2)
lb = Label(self, textvariable=self.timestr)
lb.pack(fill=X, expand=NO, pady=2, padx=2)
lc = Label(self, textvariable=self.timestr)
lc.pack(fill=X, expand=NO, pady=2, padx=2)
ld = Label(self, textvariable=self.timestr)
ld.pack(fill=X, expand=NO, pady=2, padx=2)
lsplita = Label(self, textvariable=self.lapastr)
lsplita.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitb = Label(self, textvariable=self.lapbstr)
lsplitb.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitc = Label(self, textvariable=self.lapcstr)
lsplitc.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitd = Label(self, textvariable=self.lapdstr)
lsplitd.pack(fill=X, expand=NO, pady=2, padx=2)
self._setTime(self._elapsedtime)
Now this is my Main with the grid....
def main():
root = Tk()
root.geometry("500x400")
sw1 = StopWatch(root)
sw1.grid(row=0, column=5, rowspan=2)
b1 = Button(root, text='Start', command=sw1.Start)
b2 = Button(root, text='Stop', command=sw1.Stop)
b3 = Button(root, text='Reset', command=sw1.Reset)
b4 = Button(root, text='Quit', command=root.quit)
b5 = Button(root, text='Get Split A', command=sw1.Getsplita)
b6 = Button(root, text='Get Split B', command=sw1.Getsplitb)
b7 = Button(root, text='Get Split C', command=sw1.Getsplitc)
b8 = Button(root, text='Get Split D', command=sw1.Getsplitd)
b1.grid(row=0, column=0)
b2.grid(row=0, column=1)
b3.grid(row=0, column=2)
b4.grid(row=0, column=3)
b5.grid(row=1, column=0)
b6.grid(row=1, column=1)
b7.grid(row=1, column=2)
b8.grid(row=1, column=3)
root.mainloop()
It is a best practice to use both grid
and pack
within an application.
The only caveat is that you cannot mix them within a given frame. The choice of geometry manager (grid
, pack
, place
) for widgets in a frame is completely independent of the geometry manger used in any other frame. Each has strengths and weaknesses, and you should use them accordingly.
Also -- and I see this mistake all the time -- a class that inherits from a widget should not call grid
or pack
or place
on itself.
For example, this is incorrect:
class Example(tk.Frame):
def __init__(self, parent):
...
self.pack(...)
class Main(tk.Frame):
def __init__(self, parent):
...
this.example = Example(self)
Instead of the above, pack
should be removed from Example
, and Main
should be redefined to call pack
(or grid
or place
):
class Main(tk.Frame):
def __init__(self, parent):
...
this.example = Example(self)
this.example.pack(...)
The problem with the first (incorrect) example is that if you have a dozen frames inside of main along with some other widgets, if you decide to switch from pack
to grid
, you'll have to modify a dozen other classes. By having a parent responsible for arranging it's own children, when you make a layout change, you only have to change a single function.