Reaching an Object Outside of the Class in Python - python

I have started to learn Python recently and I am trying to make a basic GUI. I stucked at some point as you imagine. I found that code from the internet (possibly in stackoverflow) and am trying to modify it for the GUI which I want to make.
All code;
# imported necessary packages etc.
class tkinterApp(tk.Tk):
# __init__ function for class tkinterApp
def __init__(self, *args, **kwargs):
# __init__ function for class Tk
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack()
container.pack_propagate(0)
# initializing frames to an empty array
self.frames = {}
# iterating through a tuple consisting
# of the different page layouts
for F in (LoadPage, StartPage):
frame = F(container, self)
# initializing frame of that object from
# startpage, page1, page2 respectively with
# for loop
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky ="nsew")
self.show_frame(LoadPage)
# to display the current frame passed as
# parameter
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def load():
try:
import data
except:
print("Error...")
# first window frame loadpage
class LoadPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
top_container = tk.Frame(self)
top_container.pack()
top_container.pack_propagate(0)
lbl_title = tk.Label(top_container, text = "TITLE")
lbl_title.pack(side="bottom")
lbl_title.pack_propagate(0)
center_container = tk.Frame(self)
center_container.pack()
center_container.pack_propagate(0)
a_image = ImageTk.PhotoImage(Image.open(r"images/image.png"))
image_area = tk.Label(center_container, image = a_image)
image_area.image = a_image
image_area.pack(side = "top", pady=50)
image_area.pack_propagate(0)
b_image = ImageTk.PhotoImage(Image.open(r"images/image.png"))
btn_load = tk.Button(center_container, image = b_image, command=lambda:[load(), controller.show_frame(StartPage)])
btn_load.image = b_image
btn_load.pack(pady=10)
btn_load.pack_propagate(0)
lbl_load_inf = tk.Label(center_container, text = "INFO...")
lbl_load_inf.pack(pady=5)
lbl_load_inf.pack_propagate(0)
bottom_container = tk.Frame(self)
bottom_container.pack()
bottom_container.pack_propagate(0)
# second window frame startpage
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
top_container = tk.Frame(self)
top_container.pack()
top_container.pack_propagate(0)
lbl_title = tk.Label(top_container, text = "TITLE")
lbl_title.pack(side="top")
lbl_title.pack_propagate(0)
toolbar = tk.Frame(top_container)
toolbar.pack(side="bottom")
toolbar.pack_propagate(0)
btn_1 = tk.Button(toolbar, text ="BUTTON_1", \
command = lambda : controller.show_frame(StartPage))
btn_1.place(relheight=1, width=220, relx=0)
btn_2 = tk.Button(toolbar, text ="BUTTON_2", \
command = lambda : controller.show_frame(StartPage))
btn_2.place(relheight=1, width=200, relx=0.18)
center_container = tk.Frame(self)
center_container.pack()
center_container.pack_propagate(0)
figure = plt.Figure()
ax = figure.add_subplot(111)
chart_type = FigureCanvasTkAgg(figure, center_container)
chart_type.get_tk_widget().place(relx = 0.10, rely = 0.3, relwidth = 0.6, relheight = 0.4)
df["Column"].value_counts().sort_values().plot(kind='barh', legend=True, ax=ax)
ax.set_title('Title')
bottom_container = tk.Frame(self)
bottom_container.pack()
bottom_container.pack_propagate(0)
# Driver Code
app = tkinterApp()
app.mainloop()
If you look at the StartPage class, there is a code block in init function;
figure = plt.Figure()
ax = figure.add_subplot(111)
chart_type = FigureCanvasTkAgg(figure, center_container)
chart_type.get_tk_widget().place(relx = 0.10, rely = 0.3, relwidth = 0.6, relheight = 0.4)
df["Column"].value_counts().sort_values().plot(kind='barh', legend=True, ax=ax)
ax.set_title('Title')
at this code block there is a DataFrame (df). It comes from data.py (imported via load function which triggered by a button in LoadPage). I said it comes from but I should have said it should come from. Unfortunately I could not get the data. DataFrame imported perfectly but somehow I cannot reach that df. How can I reach i? Possibly it is about class system but I cannot figure it out.
I got the error;
NameError: name 'df' is not defined
Note: Code is a bit mess right now, I know. I will modify more in future. I am trying to learn how Tkinter, GUI, Python works just right now. Please pardon me for that messy code :)

Related

I can't seem to pass on an object to another class in Python/Tkinter

I have an object which I want to pass to a new frame through a method in another frame class. I found a solution that looked similar to what I was after and tried it, however, the object doesn't get passed. In fact, I got an error message saying "Value after * must be an iterable, not PackingList", where PackingList is the class which the object is made out of. I tried to instead get the "name" attribute of the object and pass it to the next frame, but it just returns an empty tuple. I am really stuck and I appreciate some help.
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for F in (Homescreen, Menuscreen, Create, ShowUpcoming, ShowAll, OpenList, Search, Edit):
frame = F(container, self, *args, **kwargs)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(Homescreen)
def show_frame(self, container, *args, **kwargs):
frame = self.frames[container]
frame.tkraise()
...
class ShowAll(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
tk.Label(self, text = "Alla packningslistor", font = ("Times new roman", 30)).place(x = 110, y = 0)
self.controller = controller
objectList = PK.main()
objectOpen = PK.showAll(objectList)
self.radiobutton_list = []
self.objectList = objectList
for i in range(len(objectOpen)):
radiobutton = tk.Radiobutton(self, text = objectOpen[i], command = functools.partial(self.objectToOpen, idx = i)).place(x = 150, y = 100 + i*30)
self.radiobutton_list.append(radiobutton)
def objectToOpen(self, idx):
objectID = self.objectList[idx]
return self.controller.show_frame(OpenList, *objectID)
class OpenList(tk.Frame):
def __init__(self, parent, controller, *objectID):
tk.Frame.__init__(self, parent)
#lala = getattr(objectID, "name")
#print(lala)
As I said I tried to pass just the name of the object but it prints out as an empty tuple in the next frame class.
I did not understand your question. But here are my corrections to your App class. I hope these corrections can help your understanding of Python and tkinter and debug the rest of your codes.
If you need more detailed help, it will be helpful if you can more specific by stating in the comment section what you want to do with classes App, ShowAll, and OpenList (or their method), and I will see how I can help you by elaborating my answer further.
import tkinter as tk
class App(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__()
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.frames = {}
colors = ('white', 'black', 'red', 'green', 'blue', 'cyan', 'yellow', 'magenta')
frames = ("Homescreen", "Menuscreen", "Create", "ShowUpcoming", "ShowAll", "OpenList", "Search", "Edit")
col=0
for C, F in zip(colors, frames):
print(f'{F=}')
self.frames[F] = tk.Frame(self.container, background=C, width=100, height=50)
self.frames[F].grid(row=0, column=col, sticky="nsew")
# col += 1 # uncomment this to see all frames
self.show_frame('Create') # you should see red
def show_frame(self, container):
self.frames[container].tkraise()
if __name__ == "__main__":
app = App()
app.mainloop()

Tkinter pyplot subplot using dataframe

I have a dataframe with various values in and am trying to plot it onto a figure embedded into tkinter. Here is the code:
figure = plt.Figure(figsize=((0.8 / 3) * res_width / 100, 0.2 * res_height / 100), facecolor="#67676b")
figure.add_subplot(fc="#15151c").plot(df["Close time"][0:50], df["Close"][0:50], "-g")
Here is what is shown:
It shows a straight line, why is this, when the data clearly shows otherwise?
I ouputted Close axis as a list and here is the output:
['19183.62000000', '19184.58000000', '19185.57000000', '19183.94000000', '19202.16000000', '19213.30000000', '19199.25000000', '19176.14000000', '19186.30000000', '19179.88000000', '19179.34000000', '19178.32000000', '19187.08000000', '19181.12000000', '19200.12000000', '19202.25000000', '19209.05000000', '19208.59000000', '19200.92000000', '19205.37000000', '19205.23000000', '19205.19000000', '19200.26000000', '19199.92000000', '19198.12000000', '19200.99000000', '19204.54000000', '19210.29000000', '19210.00000000', '19209.86000000', '19219.52000000', '19222.24000000', '19253.65000000', '19241.24000000', '19255.85000000', '19257.37000000', '19250.00000000', '19239.38000000', '19236.97000000', '19246.57000000', '19230.70000000', '19229.76000000', '19228.76000000', '19229.75000000', '19231.22000000', '19227.77000000', '19229.69000000', '19209.75000000', '19211.64000000', '19232.56000000']
As you can see, the graph should go up and down and not just a straight line. Is it only plotting a trend line for some reason?
I recreated a "minimal reproducible example":
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import sys
platform = sys.platform
# root class
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
# cycle through windows
for F in (MainMenu, PageTwo):
frame = F(container)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainMenu)
# method to change frames
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class MainMenu(tk.Frame): # main menu
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.configure(bg="#15151c")
close_time = [1605549599999, 1605553199999, 1605556799999, 1605560399999, 1605563999999, 1605567599999, 1605571199999, 1605574799999, 1605578399999, 1605581999999, 1605585599999, 1605589199999, 1605592799999, 1605596399999, 1605599999999, 1605603599999, 1605607199999, 1605610799999, 1605614399999, 1605617999999, 1605621599999, 1605625199999, 1605628799999, 1605632399999, 1605635999999, 1605639599999, 1605643199999, 1605646799999, 1605650399999, 1605653999999, 1605657599999, 1605661199999, 1605664799999, 1605668399999, 1605671999999, 1605675599999, 1605679199999, 1605682799999, 1605686399999, 1605689999999, 1605693599999, 1605697199999, 1605700799999, 1605704399999, 1605707999999, 1605711599999, 1605715199999, 1605718799999, 1605722399999, 1605725999999]
close = ['16691.91000000', '16690.85000000', '16779.01000000', '16825.56000000', '16695.41000000', '16713.86000000', '16713.57000000', '16806.09000000', '16690.21000000', '16678.02000000', '16573.58000000', '16668.95000000', '16619.81000000', '16631.72000000', '16750.00000000', '16704.83000000', '16659.11000000', '16763.47000000', '16995.06000000', '17083.01000000', '16947.38000000', '17153.93000000', '17322.19000000', '17639.35000000', '17808.66000000', '17638.37000000', '17685.22000000', '17677.64000000', '17624.15000000', '17596.78000000', '17659.38000000', '17693.64000000', '17739.42000000', '17680.64000000', '18030.71000000', '18369.33000000', '17623.39000000', '17780.19000000', '18063.70000000', '18211.02000000', '18110.00000000', '18250.00000000', '18249.99000000', '18024.03000000', '17882.89000000', '17683.11000000', '17873.88000000', '17793.71000000', '17890.45000000', '17775.92000000']
# graphs
figure = plt.Figure(figsize=(100, 100), facecolor="#67676b")
figure.add_subplot(fc="#15151c").plot(close_time, close, "-g")
FigureCanvasTkAgg(figure, self).get_tk_widget().pack()
class PageTwo(tk.Frame): # second page
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.configure(bg="#15151c")
tk.Label(self, text="pg2", font=("Consolas", 40), fg="#67676b", bg="#15151c").pack()
tk.Button(self, text="Return", command=lambda: app.show_frame(MainMenu)).pack()
# launch application
app = Application()
app.title("Test App")
if platform == "linux":
app.wm_attributes("-zoomed", 1)
else:
app.state("zoomed")
app.configure(bg="black")
app.mainloop()
The issue is because your close list is a list of strings, not floats, so matplotlib is treating them as catagorical data.
close = ['16691.91000000', '16690.85000000', '16779.01000000', '16825.56000000', '16695.41000000', '16713.86000000', '16713.57000000', '16806.09000000', '16690.21000000', '16678.02000000', '16573.58000000', '16668.95000000', '16619.81000000', '16631.72000000', '16750.00000000', '16704.83000000', '16659.11000000', '16763.47000000', '16995.06000000', '17083.01000000', '16947.38000000', '17153.93000000', '17322.19000000', '17639.35000000', '17808.66000000', '17638.37000000', '17685.22000000', '17677.64000000', '17624.15000000', '17596.78000000', '17659.38000000', '17693.64000000', '17739.42000000', '17680.64000000', '18030.71000000', '18369.33000000', '17623.39000000', '17780.19000000', '18063.70000000', '18211.02000000', '18110.00000000', '18250.00000000', '18249.99000000', '18024.03000000', '17882.89000000', '17683.11000000', '17873.88000000', '17793.71000000', '17890.45000000', '17775.92000000']
Convert the items in close to floats, and it should work fine. In your minimal exmaple, something like:
close = [float(i) for i in close]
should do it.
In your original code you have a dataframe, rather than a list; you could use df["Close"][0:50].astype(float) as you plot the data in that case
figure.add_subplot(fc="#15151c").plot(df["Close time"][0:50], df["Close"][0:50].astype(float), "-g")

Tkinter: Updating Images in labels that aren't directly accessible

In my GUI, i wanted to display an image that changes depending on some value. The image would change between self.img1 and self.img2. I created separate classes for the container and the pages. The container is defined as such:
class Gui(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill = "both", expand = TRUE)
container.grid_rowconfigure(0, weight = 1)
self.MyReading = StringVar()
self.redpic = Image.open("red.png")
self.redpic = self.redpic.resize((100,100), Image.ANTIALIAS)
self.greenpic = Image.open("green.png")
self.greenpic = self.greenpic.resize((100,100), Image.ANTIALIAS)
self.img1 = ImageTk.PhotoImage(self.redpic)
self.img2 = ImageTk.PhotoImage(self.greenpic)
self.frames={}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
And the page displaying the image:
class StartPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self,parent)
label = Label(self, text="StartPage")
label.grid()
label1 = Label(self, textvariable = controller.MyReading)
label1.grid();
self.label4 = Label(self, image = controller.img1)
self.label4.grid();
self.label4.image = controller.img1
button1 = Button (self, text = "Show PageOne", command = lambda: controller.show_frame(PageOne))
button1.grid()
It is currently displaying img1. Now, to instantiate the GUI:
root = Gui()
update_reading()
root.mainloop()
update_reading() updates my other labels defined with StringVar(). I was wondering how would I go about updating label4 (which shows the image) if I can only instantiate/get access to Gui()? I only know that I could change the label4 through configure(). Is there a textvariable equivalent for images?
EDIT: I forgot to put the logic that I wanted to implement. It is basically:
If foo == TRUE:
--change the image to img1--
else:
--change the image to img2--
for some foo that exists outside of Gui.
EDIT2: Following through a previous comment's logic, I made some small changes to the code In the Gui:
class Gui(Tk):
def __init__(self, *args, **kwargs):
self.ColorVar = DoubleVar()
And within StartPage(), the changes are:
class StartPage(Frame):
def __init__(self, parent, controller):
controller.ColorVar.trace("w",self.IdkChief(controller))
def IdkChief(self, controller):
global val1
if float(val1) < 2.50 :
self.label4.configure(image = controller.img2)
self.label4.image = controller.img2
else:
self.label4.configure(image = controller.img1)
self.label4.image = controller.img1
Then the changes on ColorVar is defined in update_reading()as such:
def update_reading():
global val1
root.ColorVar.set(val1)
root.after(100,update_reading)
Where val1 is a changing float value. I decided to change it from a boolean logic to a float one to increase flexibility. It would then throw me a generic error
Exception in Tkinter callback Traceback (most recent call last):
File
"C:\Users\AppData\Local\Programs\Python\Python37\lib\tkinter__init__.py",
line 1705, in call
return self.func(*args) TypeError: 'NoneType' object is not callable
This error would repeat until the GUI is closed.
You can use tkinter variable trace function to set up a callback function to be executed whenever the variable is updated. Inside the callback function, you can then update the label based on the value of the variable.
Below is sample code blocks (based on your posted code design) to achieve your goal:
class Gui:
def __init__(self, *args, **kwargs):
...
self.ColorVar = DoubleVar()
...
class StartPage(Frame):
def __init__(self, parent, controller):
...
# register a callback to be executed whenever variable is modified
controller.ColorVar.trace('w', lambda *args: self.IdkChief(controller))
def IdkChief(self, controller):
img = controller.img1 if controller.ColorVar.get() < 2.5 else controller.img2
self.label4.config(image=img)

Tkinter: refresh frame/window from an outside function

I would like to refresh the MainPage when the updateCustomerList is finished updating the list so that this updated list is shown on the MainPage widgets.
I tried playing around with tk.show_frame(<frame>) and etc. but since the function itself isn't tied to the main Tkinter frame itself or isn't even a Tkinter object, then I'm not entirely sure how to reload the page. Any suggestions?
The code below is a snippet of my entire program:
customerList = [] #list is updated at the updateCustomerList function; global variable
class POS(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for F in (ErrorPage, PaymentPage, MainPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column = 0, sticky = "nsew")
#show frame here
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
"""
Tkinter frame that I would like to "refresh" and use the new updated list
"""
frame5Button = ttk.Button(frame5, text = "Add Item", command = lambda: updateCustomerList(barCode, quantity))
frame5Button.grid(row = 0, column = 5, padx = 90, pady = 10)
#This button allows me to go into the updateCustomerList function
def updateCustomerList(barCode, quantity):
#some code to update a list
#when function finishes updating the list, I would like to go back to the MainPage Tk frame and reload all the widgets like labels and entry boxes using the updated customerList list
app = POS()
app.geometry("700x700")
app.resizable(False, False)
app.after(100, MasterFilePopUp)
app.mainloop()
Just remove and recreate the instance of Mainframe inside as the last line of updateCustomerList:
container = 0 #global variable
Add "global container" inside POS(tk.Tk)
def updateCustomerList(barCode, quantity):
global app
...
app.frames[MainPage].destroy()
app.frames[MainPage] = MainPage(container, app)
app.frames[MainPage].grid(row=0, column = 0, sticky = "nsew")
app.frames[MainPage].tkraise()
#function ends here

Python TypeError: __init__() got multiple values for argument 'master'

Trying to build a GUI in Python at the moment, and I'm stuck at this part in particular. Every time I try to run my code it just throws the error TypeError: __init__() got multiple values for argument 'master'. I can't seem to find where I'm passing it more than one value, and it's got me scratching my head. I tried searching the error but the fixes other people had listed I can't see how to make them work with this one. Any guidance would be much appreciated. See code sample below:
class Plotter(tk.Canvas):
"""Creates a canvas for use in a GUI
Plotter() -> Canvas
"""
def __init__(self, master, **kwargs):
super().__init__(self, master = master, **kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
self.bg = 'white'
self.relief = 'raised'
class AnimalDataPlotApp(object):
"""This is the top level class for the GUI, and is hence responsible for
creating and maintaining instances of the above glasses
"""
def __init__(self, master):
"""Initialises the window and creates the base window for the GUI.
__init__() -> None
"""
master.title('Animal Data Plot App')
self._master = master
self._text = tk.Text(master)
self._text.pack
menubar = tk.Menu(master)
master.config(menu = menubar)
filemenu = tk.Menu(menubar) #puts filemenu into the menubar
menubar.add_cascade(label = 'File', menu = filemenu)
filemenu.add_command(label = 'Open', command = self.open_file)
#frame for canvas
plotter_frame = tk.Frame(master, bg = 'red')
plotter_frame.pack(side = tk.RIGHT, anchor = tk.NW, fill = tk.BOTH, expand = True)
#frame for buttons
button_frame = tk.Frame(master, bg = 'yellow')
button_frame.pack(side=tk.TOP, anchor=tk.NW, ipadx=50, fill = tk.X)
#Label on the top left
left_label = tk.Label(button_frame, text='Animal Data Sets', bg='orange')
left_label.pack(side=tk.TOP, anchor=tk.N, fill=tk.X)
#second frame, for selection list
selection_frame = tk.Frame(master, bg = 'blue')
selection_frame.pack(side = tk.LEFT, anchor=tk.NW, fill = tk.BOTH, expand = True)
#draw buttons in frame
select = tk.Button(button_frame, text ='Select')
select.pack(side=tk.TOP, anchor=tk.N)
deselect = tk.Button(button_frame, text='Deselect')
deselect.pack(side=tk.TOP, anchor=tk.N)
self.selectionbox = SelectionBox(selection_frame)
self.selectionbox.pack(side = tk.TOP, expand = True, fill=tk.BOTH)
#self._selectionbox.show_animals(self._data)
self.plotter = Plotter(plotter_frame)
self.plotter.pack(side = tk.TOP, expand = True, fill=tk.BOTH)
super().__init__(self, master = master, **kwargs)
If you're using super(), you don't need to specify self explicitly.
You are getting that error because self is being interpreted as the argument for master. So it's like if you were calling __init__(master=self, master=master, **kwargs).
The issue is in the following line in Plotter.__init__() -
super().__init__(self, master = master, **kwargs)
When calling the parent's __init__ method, you do not need to pass the self argument, when you do that, it is getting treated as the master argument for the parent, and then when you try to pass master as master=master , it causes your error.
You should simply do -
super().__init__(master = master, **kwargs)

Categories

Resources