2

I'm fairly new to python and try to build a simple GUI following an object oriented approach. Therefor I let my widget classes inherit from tk.Frame and create an application controller to build the GUI.

My application contains the following two files:

mainModule.py

# coding: utf8
from testPackage import myGUI
import Tkinter as tk

root = tk.Tk()  # Main window
my_gui = myGUI.MainApplication(root)
root.mainloop()  # Hold window open until we close it

myGUI.py

# coding: utf8
import Tkinter as tk


# Application initializer
class MainApplication(tk.Frame):
    def __init__(self, master):
         self.master = master
         tk.Frame.__init__(self, master)
         self.configure_gui()
         self.pack()    # <--- CODE IN QUESTION
         self.create_widgets()

    def configure_gui(self):
        self.master.title("Any title")
        self.master.geometry('800x600')
        self.master.minsize(600, 100)

    def create_widgets(self):
        self.main_window = MainWindow(self)
        self.main_window.pack()


# Main Data Window
class MainWindow(tk.Frame):
     def __init__(self, master):
         self.master = master
         tk.Frame.__init__(self, master)
         self.main_window = tk.Label(self, text="This is a test")
         self.main_window.pack(side="top", fill="x")

Initially running my code without self.pack() (marked as #Code in question) in the MainApplication class definition gave me only the root basic window without the Label created from MainWindow class.

As simple as the answer may be but why?

I used a few sources to get into the topic inbefore and some of them didn't use any geometry manager on the root window (or i'm to inexpierienced to see it. Source below for examples).

Source:

https://www.begueradj.com/tkinter-best-practices.html

http://python-textbok.readthedocs.io/en/latest/Introduction_to_GUI_Programming.html#putting-it-all-together

Tkinter example code for multiple windows, why won't buttons load correctly?

Only after reading following answers regarding widget creation I came aware of the missing part:

Best way to structure a tkinter application

creating a custom widget in tkinter

Figuring that pack() on initialization would be the same as

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

or

Example(root).place(x=0, y=0, relwidth=1, relheight=1)

for a grid based layout.

I guess it's a very simple or obvious element i don't see relating to inheritance but i'm not entirely sure why have to pack() the MainApplication Frame and furthermore why it seems to work for example without this step.

Kamakiri
  • 63
  • 2
  • 7
  • Using geometry managers is like showing a photo of a relative you told your friend all about. They'd still know their qualities w/o you showing them the photo, but not necessarily know how they exactly look like. – Nae Nov 15 '17 at 12:16
  • Thanks for clarifying. Just figured i misinterpreted the inheritance code of the class. – Kamakiri Nov 15 '17 at 12:50

1 Answers1

1

Because MainWindow inherits from Frame, it is itself a frame. If you never call pack, place, or grid on it, it will be invisible. This is no different than if you created a button or scrollbar or any other widget and then don't call one of those methods on it.

Since all of the other widgets are a children of this frame, they will be invisible since their parent is invisible.


Somewhat unrelated to the question being asked, self.pack() is a code smell. Generally speaking, a class should never call pack, place or grid on itself. This tightly couples itself to the caller (meaning, this widget has to know that the caller is using one of those methods).

In other words, if you decide that MainApplication wants to switch from pack to grid for all of its children, you can't just update MainApplication, you also have to update MainWindow. In this case, the root window has only one child so the problem is fairly small, but by doing it this way you are starting a bad practice that will eventually cause you problems.

The rule of thumb is that the function that creates a widget should be responsible for adding it to the screen. That means that it would be better to do it like this:

root = tk.Tk() 
my_gui = myGUI.MainApplication(root)
my_gui.pack(fill="both", expand=True)

You would then need to remove self.pack() from MainApplication.__init__.

You also have some very misleading code that might be contributing to the confusion. Take a look at this code:

# Main Data Window
class MainWindow(tk.Frame):
     def __init__(self, master):
         ...
         self.main_window = tk.Label(self, text="This is a test")
         self.main_window.pack(side="top", fill="x")

Notice how you have a class named MainWindow. Within that you have a label that is named self.main_window, but self.main_window is not a MainWindow. This is especially confusing since the function that creates MainWindow also creates an instance variable named self.main_window.

You might want to consider renaming this label to be something else (eg: self.label, self.greeting, etc).

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Re-reading my description and your answer I just figured it seems I misinterpeted the code of `MainApplication(tk.Frame)` for the type definition of a parameter instead of the class of inheritance. Feeling dumb now. Either way thanks for responding. Your answers on other topics are an excellent source to major questions for somebody like me whos only a week into python. – Kamakiri Nov 15 '17 at 12:48
  • Just read your edit. Thanks for the advice. Since i try to stick to a 'best practice' that's a great help to rooting out a major flaw in my coding style (if i may call it so) before i start a bad habit. – Kamakiri Nov 15 '17 at 12:57