TheSprinter
TheSprinter

Reputation: 358

How to avoid global variables to change widgets in Tkinter

I'm trying to write an application that offers different tools, and I want to create it in the style of CCleaner. That is, a bar of buttons to the left with different options and the rest of the window to show the options (different widgets) selected.

For the option buttons in the left bar, I'm not using Button, but Label instead, with button.bind('<Button-1>', callback) to display the appropriate widgets for the options. However, every option will have a number of widgets and, since they are all placed inside different functions in this way, I'm needing to declare those widgets as global variables, and they are a lot.

I have already worked with global variables and can be careful not to introduce bugs with them. However, I've never created anything with this number of global variables, and I'm wondering what's a better way to create a UI of this kind without them.

Here's a very basic implementation of what I'm trying to create. I didn't go further because I first want to make sure I'm on the right track, but it's enough to show what I'm doing and supplement my question.

import tkinter as tk
from tkinter import filedialog


root = tk.Tk()
root.configure(background='#929292')

screen_w = root.winfo_screenwidth()
screen_h = root.winfo_screenheight()
win_x = int((screen_w / 2) - 600)
win_y = int((screen_h / 2) - 400)


root.geometry(f'1200x700+{win_x}+{win_y}')

root.minsize(1200, 800)

def browse_files():
    browsed_files = filedialog.askopenfilenames()
    if browsed_files:
        print(browsed_files)



canvas = tk.Canvas(root, bg='white')
canvas.grid(column=1, row=0, columnspan=7, rowspan=9, sticky='nsew')
canvas.grid_columnconfigure(2, weight=1)
canvas.grid_rowconfigure(2, weight=1)
options_1_label = tk.Label(canvas, text='Tool 1 in Options 1', bg='white', padx=30)
choose_dir_label = tk.Label(canvas, text='Choose file(s)', bg='white', padx=60)
file_browse = tk.Button(canvas, text='Browse', height=1, width=15, command=browse_files)
options_2_label = tk.Label(canvas, text='Tool 1 in Options 2', bg='white', padx=30)


def options_1_click(event):
    global options_1_label
    global choose_dir_label
    global file_browse

    options_2_label.grid_forget()
    options_1_label.grid(column=0, row=0, sticky='w', pady=(30, 0))
    choose_dir_label.grid(column=0, row=1, sticky='w', pady=(30, 0))
    file_browse.grid(column=1, row=1)
    

def options_2_click(event):
    global options_2_label
    global choose_dir_label
    global file_browse


    options_1_label.grid_forget()
    options_2_label.grid(column=0, row=0, sticky='w', pady=(30, 0))
    choose_dir_label.grid(column=0, row=1, sticky='w', pady=(30, 0))
    file_browse.grid(column=1, row=1)
    


options_1_button = tk.Label(root, text="Options 1", height=2, width=20, pady=30, bg='#606060', fg='white')
options_2_button = tk.Label(root, text="Options 2", height=2, width=20, pady=30,bg='#606060', fg='white')

root.grid_columnconfigure(1, weight=1)
root.grid_rowconfigure(8, weight=1)

options_1_button.grid(column=0, row=0, rowspan=2, sticky='we')
options_1_button.bind('<Button-1>', options_1_click)

options_2_button.grid(column=0, row=2, rowspan=2, sticky='we')
options_2_button.bind('<Button-1>', options_2_click)




root.mainloop()

As you can see, some widget variables are reused. That's only for the sake of showing minimum reproducible example and asking the question.

Upvotes: 0

Views: 375

Answers (1)

Tobi208
Tobi208

Reputation: 1376

You could create your application as a class and then reference the elements as class variables:

import tkinter as tk
from tkinter import filedialog


def browse_files():
    browsed_files = filedialog.askopenfilenames()
    if browsed_files:
        print(browsed_files)


class App:

    def __init__(self):
        self.root = tk.Tk()
        self.root.configure(background='#929292')

        screen_w = self.root.winfo_screenwidth()
        screen_h = self.root.winfo_screenheight()
        win_x = int((screen_w / 2) - 600)
        win_y = int((screen_h / 2) - 400)

        self.root.geometry(f'1200x700+{win_x}+{win_y}')
        self.root.minsize(1200, 800)

        self.canvas = tk.Canvas(self.root, bg='white')
        self.canvas.grid(column=1, row=0, columnspan=7, rowspan=9, sticky='nsew')
        self.canvas.grid_columnconfigure(2, weight=1)
        self.canvas.grid_rowconfigure(2, weight=1)
        self.options_1_label = tk.Label(self.canvas, text='Tool 1 in Options 1', bg='white', padx=30)
        self.choose_dir_label = tk.Label(self.canvas, text='Choose file(s)', bg='white', padx=60)
        self.file_browse = tk.Button(self.canvas, text='Browse', height=1, width=15, command=browse_files)
        self.options_2_label = tk.Label(self.canvas, text='Tool 1 in Options 2', bg='white', padx=30)

        def options_1_click(event):

            self.options_2_label.grid_forget()
            self.options_1_label.grid(column=0, row=0, sticky='w', pady=(30, 0))
            self.choose_dir_label.grid(column=0, row=1, sticky='w', pady=(30, 0))
            self.file_browse.grid(column=1, row=1)

        def options_2_click(event):

            self.options_1_label.grid_forget()
            self.options_2_label.grid(column=0, row=0, sticky='w', pady=(30, 0))
            self.choose_dir_label.grid(column=0, row=1, sticky='w', pady=(30, 0))
            self.file_browse.grid(column=1, row=1)

        self.options_1_button = tk.Label(self.root, text="Options 1", height=2, width=20, pady=30, bg='#606060', fg='white')
        self.options_2_button = tk.Label(self.root, text="Options 2", height=2, width=20, pady=30, bg='#606060', fg='white')

        self.root.grid_columnconfigure(1, weight=1)
        self.root.grid_rowconfigure(8, weight=1)

        self.options_1_button.grid(column=0, row=0, rowspan=2, sticky='we')
        self.options_1_button.bind('<Button-1>', options_1_click)

        self.options_2_button.grid(column=0, row=2, rowspan=2, sticky='we')
        self.options_2_button.bind('<Button-1>', options_2_click)

    def run(self):
        self.root.mainloop()


App().run()

Upvotes: 2

Related Questions