0

When implementing complex dialogs (i.e. dialogs with some 10 or more widgets, especially when arranged within multiple frames or the like), the creation requires many tkinter calls and the code can become increasingly complex (difficult to read and maintain) when it is kept within a single method. Also in general, short functions/methods are usually preferred over longer ones.

My current approach to limit method length is to encapsulate creation of all widgets that belong to a group within the dialog into one method(parent_frame, other_options) that returns the top-level widget like this:

import tkinter as tk

class Dialog:
    def __init__(self, master):
        self.__master = master
        self.create_gui(master)

    def create_gui(self, frame, title = None):
        if title:  frame.title(title)

        group_a = self.create_group_a(frame)
        group_a.grid(row=0, column=0, sticky="nsew")

        group_b = self.create_group_b(frame)
        group_b.grid(row=1, column=0, sticky="nsew")

        frame.columnconfigure(0, weight=1)
        frame.rowconfigure(0, weight=1)
        frame.rowconfigure(1, weight=1)

    def create_group_a(self, frame):
        inner_frame = tk.LabelFrame(frame, text="Label")
        text = self.create_text_with_scrollbar(inner_frame)
        text.pack(fill="both")
        return inner_frame

    def create_group_b(self, frame):
        button = tk.Button(frame, text="Button")
        return button

    def create_text_with_scrollbar(self, frame):
        text_frame = tk.Frame(frame)
        text_frame.grid_rowconfigure(0, weight=1)
        text_frame.grid_columnconfigure(0, weight=1)

        text = tk.Text(text_frame)
        text.grid(row=0, column=0, sticky="nsew")

        scrollbar = tk.Scrollbar(text_frame, command=text.yview)
        scrollbar.grid(row=0, column=1, sticky="nsew")
        text['yscrollcommand'] = scrollbar.set

        return text_frame


if __name__ == "__main__":
    master = tk.Tk()
    Dialog(master)
    tk.mainloop()

Are there any specific guidelines around on code structuring in such cases? Does anybody have any advice on how to better structure such code?

Robin
  • 153
  • 1
  • 9

1 Answers1

2

What I usually do is to write a new class for every group. Those classes inherit from Frame. The end result will look something like this:

class MainFrame(Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self.first_sub = FirstSubFrame(self)
        self.second_sub = SecondSubFrame(self)

        self.first_sub.grid()
        self.second_sub.grid()


class FirstSubFrame(Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self.possibly_another_subframe = PossibleOtherFrame(self)
        self.awesome_button = tkinter.Button()

        self.possibly_another_subframe.grid()
        self.awesome_button.grid()

...

I hope this helps.

Matthias Schreiber
  • 2,347
  • 1
  • 13
  • 20
  • Thanks! Brian Oakley also inherits his classes from Tkinter classes in http://stackoverflow.com/questions/17466561/best-way-to-structure-a-tkinter-application. However there is a comment to his answer (from Shule) stating that in some cases it may be better to not inherit from Frame etc but rather have a frame member. My intuition tells me the same, but I cannot give an explanation why. This was also one reason to ask the question in the first place. I will update the question. – Robin May 11 '16 at 08:47
  • No, I will not update the question. The question "inheriting from tkinter classes or not" is a topic on its own which has been discussed, eg. here: http://stackoverflow.com/questions/7300072/inheriting-from-frame-or-not-in-a-tkinter-application. – Robin May 11 '16 at 08:59