I am using Python 2.7 and Tkinter. I am almost new to Object Oriented programs. I have a long program with many Tkinter windows and at some point I ask the user to load an Excel file that I read with Pandas, and want to permanently use and update that value (of a data variable). The way that I am doing it now is with global variables but I know that it is dangerous, inefficient and not elegant at all.
Even though I could do controller.show_frame(framename) given the way my gui class is built, I ended up building some of the frames myself just so the data variable would update itself.
I read and tried some answers in Stack Overflow but may have implemented them wrong:
- Tried creating a dictionary inside the gui class, something like
self.app_data = {data=[],filename=""}
and updating it from other windows, the thing here is that I think that the class gui is instanced only once and it kind of creates all of the other window classes so this did not work. Maybe I did something wrong there. (not shown on the code). - Tried to do something as what was suggested here but I could just not make it work.
Main frame is some sort of intermediate step that I need for other purposes; the following code is a simplification of my program.
I know this is an awful nightmare code! Thank you :)
import Tkinter as tk
import pandas as pd
import tkFileDialog
import tkMessageBox
global data, strat_columns, filename
data = pd.DataFrame([])
strat_columns = []
filename = ""
class gui(tk.Tk):
data = pd.DataFrame([])
filename = ""
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack(side="top",fill="both",expand=True)
self.frames = {}
for F in (main_frame, first_frame):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(main_frame)
def show_frame(self,sel_frame):
frame = self.frames[sel_frame]
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
class main_frame(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.parent = parent
self.controller = controller
button_new = tk.Button(self,
text="New window",
command=lambda: self.button_new_callback())
button_new.pack()
def button_new_callback(self,*args,**kwargs):
self.controller.show_frame(first_frame)
class first_frame(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller = controller
self.parent = parent
self.show_frame = controller.show_frame
statusText.set("Press Browse button and browse for file, then press the Go button")
label = tk.Label(self, text="Please load a file: ")
label.pack()
entry = tk.Entry(self, width=50)
entry.pack()
button_go = tk.Button(self,
text="Go",
command=lambda: self.button_go_callback(entry,statusText,message))
button_browse = tk.Button(self,
text="Browse",
command=lambda: self.button_browse_callback(entry))
button_go.pack()
button_browse.pack()
message = tk.Label(self, textvariable=statusText)
message.pack()
def button_browse_callback(self,entry):
global filename
filename = tkFileDialog.askopenfilename()
entry.delete(0, tk.END)
entry.insert(0, filename)
def button_go_callback(self,entry,statusText,message):
global data
input_file = entry.get()
data = pd.read_excel(filename)
sf = second_frame(self.parent, self)
sf.grid(row=0, column=0, sticky="nsew")
sf.tkraise()
class second_frame(tk.Frame):
pass
if __name__ == "__main__":
my_gui = gui()
my_gui.mainloop()
my_gui.title("TEST")
There are a few things that are causing issues with your program from running properly.
The first thing I noticed is the use of global variables. This can be avoided with the use of class attributes.
For the 2 variables you have just below the line
class gui(tk.Tk):
you need to move them to the__init__
section so those variables can be instantiated. Also you need to make them into class attributes so other methods or even other classes can interact with them. We can do this by adding theself.
prefix to the variable names.Something like the below:
To access the methods/attributes of the
gui
class you need to pass the object of thegui
class to the other classes working with it.statusText
in your code is not defined soset()
wont work here. Just addself.statusText
as a class attribute.Some of your widgets do not need to be assigned to a variable name as no editing is being done to them. For example:
This can be simply changed to:
This will help reduce the amount of code you are writing and keep the name space cleaner.
There are a few ways to correct all this but the easiest way is to move this code into one class. There is not a good reason with the code you have presented to have several frames separated from the main
gui
class.The below code is a rewritten version of your code using one class to accomplish what appears to be the task your code is trying to accomplish and reducing the amount of code needed by around 30+ lines. I am sure it can be refined further but this example should be helpful.
in my opinion your are tying too much the data and the GUI. What if in the future you want to display something else? I would use a more generic approach: I would a create a
DataProvider
class that would read and return the data for you:Data Provider
Using this class you can obtain the data that you can present in your GUI:
gui.py
If all works well you should have a small GUI showing the content of the Excel file and the first few bytes of the image.
Let me know if all works fine or if you need more info.
Thanks