As stated in the title, how do I prevent the use of global variables in Tkinter? I have started to learn Tkinter and am running into a problem in almost all of my projects. The problem I run into is that most Widgets need to interact with each other creating a lot of dependencies. Below is the code from one of my projects that has this problem. I included all the code from this project because the problem arises throughout the code.
As an example consider the apply_filter function. In order to receive the input from the user, the Apply Filter button's command function needs access to the entry fields. One way this can be solved is by passing all the entry fields to the command function as arguments. However, I don't like this option because this means that I either have to place all the code into on giant block making the code more difficult to read or I have to pass it through multiple functions which leads to the same result. The option I chose here was to make the inputs global so that I can access them from anywhere within my code. At least the code is easier to read then. Of course this is probably not a good solution. When the GUI scales up this will create a giant mess of dependencies that could easily lead to bugs.
Long story short, I want to know: Is there a better way to deal with these dependencies so that I don't need to create tons of global variables or create unreadable code? Thanks in advance for any effort taken.
import tkinter as tk
from tkinter import filedialog as fd
import pandas as pd
import numpy as np
import sys
start_filter_row = 0
start_filter_column = 0
data_location = ""
df = None
root = None
filterr = {"customercode":{"column name": "Customer (code)", "value": None, "row_number": start_filter_row},
"partnr":{"column name": "Part nr", "value": None, "row_number": start_filter_row 1},
"partclass":{"column name": "Part class", "value": None, "row_number": start_filter_row 2},
"partfamily":{"column name": "Part familiy", "value": None, "row_number": start_filter_row 3},
"startdate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row 4},
"enddate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row 5}
}
inputs = dict()
'''
Load Data
'''
def load_dataset():
global df
root.update()
database_location = fd.askopenfilename()
df = load_data(database_location)
def load_data(database_location):
try:
df = pd.read_pickle(database_location)
return df
except Exception as e:
print("Data location not set!", e)
root.destroy()
sys.exit()
'''
create Widgets
'''
def create_filter_inputs():
for key, value in filterr.items():
label = tk.Label(root, text = key)
label.grid(row = value["row_number"], column = start_filter_column, sticky = "w")
entry = tk.Entry(root)
entry.focus_set()
entry.grid(row = value["row_number"], column = start_filter_column 1, sticky = "w")
inputs[key] = entry
def create_filter_buttons():
apply_button = tk.Button(root, text = "Apply Filter", command = apply_filter)
apply_button.grid(row = start_filter_row len(filterr), column = 0, sticky = "news")
clear_button = tk.Button(root, text = "Clear Filter", command = clear_filter)
clear_button.grid(row = start_filter_row len(filterr), column = 1, sticky = "news")
'''
Button functions
'''
def clear_filter():
for key, entry in inputs.items():
entry.delete(0, 'end')
apply_filter()
def apply_filter():
for key, entry in inputs.items():
filterr[key]["value"] = entry.get().split(";")
def main():
global data_location, root
'''
Setting up Tkinter Frame and loading dataset
'''
root = tk.Tk()
root.title("Data Tool V0.1")
loading_label = tk.Label(root, text = "Loading...", width = 30, height = 5)
loading_label.pack()
load_dataset()
loading_label.destroy()
n_rows, n_cols = (4, 3)
root.columnconfigure(tuple(range(n_cols)), weight=1)
root.rowconfigure(tuple(range(n_rows)), weight=1)
'''
Setting up GUI
'''
create_filter_inputs()
create_filter_buttons()
root.mainloop()
if __name__ == "__main__":
main()
CodePudding user response:
You can avoid using globals by converting your GUI into a class format and making them class variables instead.
class GUI:
def __init__():
.....
self.myvariable = ...
def function():
newvar = self.myvariable - 5
Its also worth noting that variables defined outside of functions can be seen from within functions, but not vice versa:
>>> def f(a):
... print(a)
...
>>> b=2
>>> f(b)
2