1

I have a GUI in Tkinter that is becoming more complicated. I would like to separate it into some modules to make it easier to manage. Is there a way to separate my GUI into modules if I didn't use the object oriented approach?

Here is a simple example of some code I would like to move to a separate module:

def create_main_nav_buttons(strat_folder_list):
    global dynamic_info_nav_items
    temp_count = 0
    for item in strat_folder_list:
        main_nav = tk.Canvas(Nav_Frame_Top, width=175, height=grid_box_size/1.5, highlightthickness=1, bg='slategray1')
        main_nav.grid(row = temp_count, column = 1)
        main_nav.bind("<Button-1>", lambda event, x=item: create_navigation2(x)) 
        temp_count += 1
    dynamic_info_nav_items.append(main_nav)

-Side note:

I wrote a GUI using the object oriented approach before but decided to not use it this time because I didn't fully understand parts of it such as:

def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)
    self.parent = parent

So when something went wrong it was a nightmare to fix and I couldn't find much support.

lessharm
  • 65
  • 13
  • (global variables and imports are going to be a nightmare too with this approach). i.e. in the sample code, you need to import `create_navigation2` and `Nav_Frame_Top` too – PRMoureu Jul 12 '17 at 20:12
  • I wrote a 600 line GUI with tkinter using functions and variables and it worked fine however I did end up moving it into a class so I could avoid using globals. You might just want to use the OOP method with classes so you can use class attributes to avoid globals. – Mike - SMT Jul 12 '17 at 20:34
  • What about the `__init__` statement do you not understand? It is not very difficult to figure out if you read the documentation on it. – Mike - SMT Jul 12 '17 at 20:36
  • Its not just that. Its how to access different functions. For example I have a button and I want it to activate a function when clicked. It doesn't work because: AttributeError: object has no attribute 'parent' I have no clue WTF is wrong. Through trial and error ill sometimes get it to work using self.parent.main. or self.main or self.. but then ill run into another problem that doesn't make sense to me. When I want to post a simplified version for a question I don't know what to take out and what to include. It sucks, I really want to use classes but idk how to get help when stuff goes bad. – lessharm Jul 20 '17 at 01:53

1 Answers1

2

Using OO techniques is the proper way to divide code into modules, but it's not the only way. I think it's a bit more work to do it without classes, but there's nothing preventing you from doing what you want without classes.

The main thing you need to change about the way you are coding is to stop relying on global variables. That means you need to pass in information from the main program to the functions, and the functions need to return information back to the main program.

Example

I've tried to keep the code in this example as close to your original as possible, though I've made a few small changes.

First, create a file named "widgets.py" with the following contents:

import tkinter as tk

def create_main_nav_buttons(parent, grid_box_size, items):
    temp_count = 0
    widgets = []
    for item in items:
        main_nav = tk.Canvas(parent, width=175, height=grid_box_size/1.5, highlightthickness=1, bg='slategray1')
        main_nav.grid(row = temp_count, column = 1)
        main_nav.bind("<Button-1>", lambda event, x=item: create_navigation2(x)) 
        temp_count += 1
        widgets.append(main_nav)

    return widgets

def create_navigation2(x):
    print("create_navigation2...", x)

Next, create your main program in a file named "main.py":

import tkinter as tk
from widgets import create_main_nav_buttons

root = tk.Tk()
Nav_Frame_Top = tk.Frame(root)
Nav_Frame_Top.pack(side="top", fill="x")

dynamic_info_nav_items = []
strat_folder_list = ["one", "two", "three"]
grid_box_size=10

widgets = create_main_nav_buttons(Nav_Frame_Top, grid_box_size, strat_folder_list)
dynamic_info_nav_items += widgets

root.mainloop()

Notice that the parent and the other items that you were expecting to be in the global namespace are passed in to the function, and then the function returns values which the main program can use.

In essence you are creating a contract between the function and the main program. The contract is "you tell me the information I need to construct the widgets, and I'll create the widgets and return them to you in a list".

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Sorry no way to private message you on here and couldn't find your contact. Have to say thanks for all your help with tkinter. All your posts are invaluable. I learned to program a little over a year ago and with your help made an extremely useful application . I know I have a lot to learn and all your posts are slowly moving me in the right direction. Thanks! – lessharm Jul 14 '17 at 17:03