Related
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()
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")
I'm trying to call a String from a entry box in another class to a public Var. The code for calling the Var seams to work fine but got a Error ''' image "pyimage5" doesn't exist''' on my second class sins i added the new lines of code. Everything was working fun before.
Lines of code I added:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent,bg=Gray_Back_Page_1)
to
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super(StartPage,self).__init__()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
super(PageOne,self).__init__(parent,bg=Gray_Back_Page_1)
and added the following code outside a class
StartPage_object = StartPage(tk.Frame, SeaofBTCapp)
USER = StartPage_object.Username_Text.get()
PASSWORD = StartPage_object.Password_Text.get()
The image code Var = Image.open(Image_File +"\File_Name.png") seams to work in my Start Page but gives me an Error in my Page One
Please see complete code below:
desktop = os.path.expanduser("~\Desktop")
Image_File = os.path.expanduser("~\Desktop\file")
#===============Frame==========================Container====================>
class SeaofBTCapp(tk.Tk,object):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Name")
tk.Tk.geometry(self,"1360x728")
tk.Tk.iconbitmap(self, default= desktop + "\icon.ico")
self.resizable(False, False)
#tk.Tk.overrideredirect(False)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=10)
container.grid_columnconfigure(0, weight=10)
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()
#===============Start=========================Page====================>
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super(StartPage,self).__init__()
#=============Set_Background==================>
load1 = Image.open(Image_File +"\Login_Bcakground.png")
render1 =ImageTk.PhotoImage(load1)
#========= Reset_username & Password ===========>
self.Username_Text= tk.StringVar()
self.Password_Text= tk.StringVar()
#==== Creating Buttons, Entry Box and Labels with there commands =====>
User_Name_Entry = tk.Entry(self,textvariable = self.Username_Text, fg =
Entry_Box_Text_Colour,bg =
Entry_Box_Back_White,borderwidth = 0,
font=Normal_Text,width = 30)
User_Name_Entry.place(x=795,y=282)
User_Pass_Entry = tk.Entry(self,textvariable = selfPassword_Text, fg =
Entry_Box_Text_Colour,bg =
Entry_Box_Back_White,borderwidth = 0,
font=Normal_Text,width = 30)
User_Pass_Entry.place(x=795,y=329)
#===============Login==========================Var====================>
StartPage_object = StartPage(tk.Frame, SeaofBTCapp)
USER = StartPage_object.Username_Text.get()
PASSWORD = StartPage_object.Password_Text.get()
#===============Page==========================One====================>
class PageOne(tk.Frame):
def __init__(self, parent, controller):
super(PageOne,self).__init__(parent,bg=Gray_Back_Page_1)
photo = ImageTk.PhotoImage(Image.open(Image_File +"\Splach_Page.png"))
vlabel=tk.Label(self,text = "",image=photo)
vlabel.image = photo
vlabel.place (x=-1,y=-5,relwidth=1, relheight=1)
Error Lines:
image "pyimage5" doesn't exist
Stack trace:
> File "C:\Users\MainUser\source\v_1_1.py", line 251, in __init__
> vlabel=tk.Label(self,text = "",image=photo)
> File "C:\Users\MainUser\source\v_1_1.py", line 93, in __init__
> frame = F(container, self)
> File "C:\Users\MainUser\source\v_1_1.py", line 2736, in <module>
> app = SeaofBTCapp()
Loaded '__main__'
for me, the problem was with Spyder not the code.
I changed to Jupyter Notebook and it worked as expected
This looks like a known bug in tkinter, where the python garbage collector deletes the photo. To prevent this, you need to create a global reference to the image. Something like this should work:
class PageOne(tk.Frame):
images = []
def __init__(self, parent, controller):
super(PageOne,self).__init__(parent,bg=Gray_Back_Page_1)
photo = ImageTk.PhotoImage(Image.open(Image_File +"\Splach_Page.png"))
PageOne.images.append(photo)
vlabel=tk.Label(self,text = "",image=photo)
vlabel.image = photo
vlabel.place (x=-1,y=-5,relwidth=1, relheight=1)
Hope that works for you!
This did not work and give me the same error, but your idea helped me to solve it in another way. I went back to my original code and solved my first issue with the list methods.
#=============Use_List_For_Capturing_Var_In_Classes==================
Name_oF_User_Loged_In = []
Name_oF_User_Loged_In.append("User Name")
class StartPage(tk.Frame):
global login_data_Pass
global login_data_User
global Name_oF_User_Loged_In
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
#=============Set_Background==================>
load1 = Image.open(Image_File +"\Login_Bcakground.png")
render1 =ImageTk.PhotoImage(load1)
def Login_Check():
USER1 = self.Username_Text.get()
PASSWORD1 = self.Password_Text.get()
login_data_Pass.pop(0)
login_data_User.pop(0)
login_data_Pass.append(PASSWORD1)
login_data_User.append(USER1)
#==== Creating Buttons, Entry Box and Labels with there commands =====>
Login_button = tk.Button(self, text="Login",
fg="#d0cece",bg="#3f9a84",borderwidth = 0, font=Normal_Text,height
=1,width = 10,
command=lambda: Login_Check())
Login_button.place(x=1010,y=380)
User_Name_Entry = tk.Entry(self,textvariable = self.Username_Text, fg =
Entry_Box_Text_Colour,bg =
Entry_Box_Back_White,borderwidth = 0,
font=Normal_Text,width = 30)
User_Name_Entry.place(x=795,y=282)
User_Pass_Entry = tk.Entry(self,textvariable = selfPassword_Text, fg =
Entry_Box_Text_Colour,bg =
Entry_Box_Back_White,borderwidth = 0,
font=Normal_Text,width = 30)
User_Pass_Entry.place(x=795,y=329)
class PageOne(tk.Frame):
global login_data_Pass
global login_data_User
global Name_oF_User_Loged_In
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent,bg=Gray_Back_Page_1)
photo = ImageTk.PhotoImage(Image.open(Image_File +"\Splach_Page.png"))
vlabel=tk.Label(self,text = "",image=photo)
vlabel.image = photo
vlabel.place (x=-1,y=-5,relwidth=1, relheight=1)
user = login_data_User[0]
password = login_data_Pass[0]
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)
So I'm trying to handle two windows in python tkinter that can edit an xml file containing the path to certain images, the problem is the function that reads the file is in the __init__ of my two class windows so whenever I switch between the windows the same images appear. My question is: is there any way to relaunch the classes so that the __init__ will run?
I believe the problem is within the show_frame function since tkraise does not run the functions in the class but just pops whatever is in the class to the top.
class xmleditApp(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 (FirstWindow, SecondWindow):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(FirstWindow)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
This is the class that I want to update automatically when I return to it:
class FirstWindow(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
secondwindow=tk.Button(self,text="edit xml",command=lambda: controller.show_frame(SecondWindow))
secondwindow.grid(row=3,column=0)
def getimagepath():
doc = parse('pruebasXML.xml')
paths = doc.getroot()
path = paths.findall('path')
pasthlist = []
for elem in range (0,len(path)):
pathlist+=[[]]
for tag in range (0,11):
pathlist[elem]+=[path[elem][tag].text]
return createbutton(pasthlist)
def createbutton(lst):
for elem in range(0,len(lst)):
buttonName=elem
photo=tk.PhotoImage(file=lst[elem][0]+'.gif')
buttonName=tk.Button(self, image=photo)
buttonName.grid(row=2, column=elem)
buttonName.image=photo
if os.path.exists('pathsXML.xml')==True:
getimagepath()
If you have a class called myclass, you can manually call __init__ by using myclass.__init__(self, etc). Here, self is the self argument, and etc are the other arguments.