1

Note

The example code is an abridged version but can run with a basic function. Please focus on class MainWindow(tk.Frame)

Questions

What kind of object can play the role of the parent or master for tkinter class initialization?

In my case, see the example code, why can not pass self as parent to ProjectInfo(...) or ConfirmItems(...) in class MainWindow(tk.Frame)? It would pop up an empty window if using self instead of self.parent.

Reference

This question originates from my comments on Best way to structure a tkinter application, which means it fails to pass self as parent.

I would like to issue a new question to follow that discussions.

import tkinter as tk
from tkinter import messagebox
from collections import OrderedDict

class ProjectInfo(tk.Frame):
    def __init__(self, parent, controller, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.parent = parent
        self.controller = controller
        self.widgets_init()

    def widgets_init(self):
        tk.Label(self,
                    text = "Rock Controller!",
                    width = 10,
                    anchor = "w",
                    justify = "left").grid(row = 0, column = 0)
        tk.Label(self,
                    text = "Input Name: ").grid(row = 1, column = 0)
        self.entry = tk.Entry(self)
        self.entry.grid(row = 1, column = 1)

class ConfirmItems(tk.Frame):
    def __init__(self, parent, frames, controller, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.parent = parent
        self.frames = frames
        self.controller = controller
        self.widgets_init()

    def update_entries(self):
        self.controller.project_info.name = self.controller.project_info.entry.get()

    def update_frames(self):
        self.message = 'Click Cancel go back to reset!\n'
        for key, values in self.frames.items():
            for v in values:
                x = getattr(key, v)
                self.message += v + ': ' + str(x) + '\n'

    def show_settings(self):
        answer = tk.messagebox.askokcancel("Check Settings", self.message)
        if answer in ["yes", 1]:
            self.quit()

    def combine_funcs(self, *funcs):
        def combined_func(*args, **kwargs):
            for f in funcs:
                f(*args, **kwargs)
        return combined_func

    def widgets_init(self):
        self.cancel = tk.Button(self,
                                text = "Cancel",
                                command = self.quit)
        self.cancel.grid(row = 0, column = 0)

        self.submit = tk.Button(self,
                                text = "Submit",
                                command = self.combine_funcs(
                                                            self.update_entries,
                                                            self.update_frames,
                                                            self.show_settings))
                                # command = lambda:[self.update_frames(), self.show_settings()]
        self.submit.grid(row = 0, column = 1)

class MainWindow(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.parent = parent
        self.controller = self

        self.project_info = ProjectInfo(self.parent, self.controller)
        self.project_info.grid(row = 0)
        self.widgets_init()

        self.confirm_items = ConfirmItems(self.parent, self.frames, self.controller)
        self.confirm_items.grid(row = 1)

    def widgets_init(self):
        self.dict_list = [(self.project_info, ('name',))]
        self.frames = OrderedDict(self.dict_list)

def main():
    root = tk.Tk()
    root.title("Welcome to Controller World!")
    root.geometry("300x300")
    gui = MainWindow(root)
    root.mainloop()

if __name__ == "__main__":
    main()
Kuo
  • 385
  • 1
  • 4
  • 14
  • 1
    You didn't layout the `MainWindow` object, thererfore all widgets with `parent=self` are hidden. – stovfl Nov 23 '19 at 13:02

1 Answers1

2

What kind of object can play the role of the parent or master for tkinter class initialization?

Any tkinter widget can play the role of parent or master, though most often it will be the root window, a toplevel window, or a frame.

In my case, see the example code, why can not pass self as parent to ProjectInfo(...) or ConfirmItems(...) in class MainWindow(tk.Frame)?

There is nothing in your code preventing you from passing self as the parent.

It would pop up an empty window if using self instead of self.parent.

In the case of using self as the parent for ProjectInfo, self refers to the instance of MainWindow. I don't see anywhere where you are calling pack or grid on that instance. If the parent is not visible, none of its children will be visible.

You need to call pack or grid on the gui variable. Since it is designed to be the only widget in the root window, pack makes the most sense.

def main():
    ...
    gui = MainWindow(root)
    gui.pack(side="top", fill="both", expand=True)
    ...

As a general rule of thumb, if you have a class that inherits from a widget and it creates other widgets, those widgets should always be children of self or one of its descendants, never its own parent. Having a class put child widgets in its parent completely defeats the purpose of inheriting from tk.Frame.

The whole point of inheriting from tk.Frame is to create a new class that behaves exactly like standard widgets. You can add it to other widgets with pack or place or grid, it has some standard options such as borderwidth and relief, and is entirely self-contained.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Or I can add one line `self.grid(row = 0)` in `__init__(...)` of `class MainWindow(tk.Frame)`. And now I further understand why we create a subclass of `tk.Frame` or other standard widget like `Toplevel`. It is application specific. We just wrap some more customized structures/attributes/functions into `one self-contained Unit` which also inherits the properties of its `parent`. – Kuo Nov 23 '19 at 16:52
  • 1
    @Kuo: it's a bad practice to have a custom widget class call `grid` or `pack` on itself. As a general rule of thumb, the code that creates a widget should be responsible for making it visible. However, there's nothing preventing you from doing it if you want. – Bryan Oakley Nov 23 '19 at 16:56
  • You are right. It's a really bad practice. Widget itself no way knows in advance where it belongs. – Kuo Nov 23 '19 at 17:04