How do I pass variables in different classes? - python

I am having trouble with passing the variables into different classes. I am a beginner. I am trying to get the entry box to pass the textvariable which is self.margaritapizza(). However, when I try run it, it says 'nextage' has no attribute to 'margaritapizza'. So not sure how to pass the variable to different classes. I am using tkinter and it is in visual studio code if that helps.
import tkinter as tk
from tkinter import IntVar
class SampleApp(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.margaritapizza = IntVar()
self.frames = {}
for F in (StartPage, nextpage):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
'''Show a frame for the given page name'''
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent, bg='#3d3d5c')
self.controller = controller
heading_label = tk.Label(self,
text="start page",
font=('arial', 45, 'bold'),
foreground='#ffffff',
background='#3d3d5c')
heading_label.pack(pady=25)
def next():
controller.show_frame('nextpage')
next_button = tk.Button(text="start page",command=next).pack
class nextpage(tk.Frame,SampleApp):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent, bg='#3d3d5c')
self.controller = controller
heading_label = tk.Label(self,
text="start page",
font=('arial', 45, 'bold'),
foreground='#ffffff',
background='#3d3d5c')
heading_label.pack(pady=25)
entry = tk.entry(borderwidth=2,width=15,textvariable=self.margaritapizza).pack
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

First, nextpage should definitely not inherit from SampleApp. So, change the definition to:
class nextpage(tk.Frame):
Second, margaritapizza is an attribute of SampleApp, so you need to refer to it via the instance of SampleApp. In your case, self.controller is that instance, so you can use self.controller.margaritapizza.
There are other problems with how you define the entry, I'll fix them as well:
self.entry = tk.Entry(self, borderwidth=2,width=15,textvariable=self.controller.margaritapizza)
self.entry.pack()

Related

Updating tkinter label in a frame object

I have been working to improve my Tkinter skills through a combination of books, on-line tutorials, and utilizing questions and examples from this site. In one script I am working to understand and build upon this question and popular answer:
Switch between two frames in tkinter.
I have created a version that allows the creation of multiple instances of the pages, tracks the number of time page 1 has been selected for each instance, and updates the label for page 1 with the number of times page 1 has been selected. To reduce the size of the program the code shown is only for the updating of the page 1 label. I have only been able to accomplish this through the use of a global variable in the 'PageOne' object and the method that is called to show it. I feel
there must be a better way but all of the ways I tried yields an error similar
to : #AttributeError: '_tkinter.tkapp' object has no attribute 'label'. Should be simple I thought but I spent a couple of days on this issue alone.
import tkinter as tk
from tkinter import font as tkfont
'''
original post
https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
'''
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
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.Label1_Text = tk.StringVar()
self.PageOne_Ct = tk.IntVar()
#-------------------------------
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def get_lbl_text(self):
label1text = self.Label1_Text.get()
return label1text
def get_page1_cts(self):
counts = self.PageOne_Ct.get()
return counts
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: PageOne.Show_PageOne(self,
parent, controller))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
global label
self.controller = controller
label = tk.Label(self, text = self.controller.Label1_Text.get(),
font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
def Show_PageOne(self, parent, controller):
global label
count = self.controller.get_page1_cts()
self.controller = controller
count = self.controller.PageOne_Ct.get()
count += 1
self.controller.PageOne_Ct.set(count)
new_label1_text = "You have clicked page one {} times".format(count)
self.controller.Label1_Text.set(new_label1_text)
label.configure(text=new_label1_text)
controller.show_frame("PageOne")
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label2 = tk.Label(self, text="This is page 2", font=controller.title_font)
label2.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Here is an example that doesn't require passing a bunch of vars around.
import tkinter as tk
'''
PREVIOUS|NEXT DISPLAY FOR PAGES
'''
class Book:
def __init__(self, master, pages):
#init page vars
self.pnum = 0
self.pages = pages
self.page = self.pages[self.pnum].show()
#next/prev buttons
tk.Button(master, text='prev', command=self.prev).place(relx=.05, rely=.95, anchor='center')
tk.Button(master, text='next', command=self.next).place(relx=.95, rely=.95, anchor='center')
def next(self):
self.page.hide()
self.pnum = (self.pnum+1)%len(self.pages)
self.page = self.pages[self.pnum].show()
def prev(self):
self.page.hide()
self.pnum = range(len(self.pages))[self.pnum-1]
self.page = self.pages[self.pnum].show()
'''
GENERIC PAGE FOR ALL PAGES TO INHERIT FROM
'''
class Page(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.visits = 0
self.label = tk.Label(self, text=f'visits: {self.visits}', font='consolas 16 bold', fg='white', bg=self['bg'])
self.label.grid()
def show(self) -> tk.Frame:
self.visits += 1
self.label.configure(text=f'visits: {self.visits}')
self.grid(row=0, column=0, sticky='nswe')
return self
def hide(self):
self.grid_forget()
'''
EXAMPLE PAGES
'''
class Page_1(Page):
def __init__(self, master, **kwargs):
Page.__init__(self, master, **kwargs)
class Page_2(Page):
def __init__(self, master, **kwargs):
Page.__init__(self, master, **kwargs)
class Page_3(Page):
def __init__(self, master, **kwargs):
Page.__init__(self, master, **kwargs)
'''
ROOT
'''
class Root(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Pages Example")
self.geometry('800x600+100+100')
#make the page span the entire display
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
#create book
Book(self, [Page_1(self, bg='blue') ,
Page_2(self, bg='red') ,
Page_3(self, bg='green'),
])
self.mainloop()
Root() if __name__ == "__main__" else None

How to update listbox when I change view in Tkinter?

I need to update listbox when I'm changing view, but I dont know how to do it. On first page I'm adding some items to list and on second it should to show all items in listbox.
# -*- coding: utf-8 -*-
from tkinter import *
tb1 = [["Kofola", "0,5","30"]]
class SeaofBTCapp(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)
container.grid_columnconfigure(0, weight=1)
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()
class StartPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self,parent)
label = Label(self, text="Start Page")
label.pack(pady=10,padx=10)
button = Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = Button(self, text="add",
command=self.add)
button2.pack()
def add(self):
tb1.append(["Radegast", "0,5","30"])
print(tb1)
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = Label(self, text="Page One!!!")
label.pack(pady=10,padx=10)
button1 = Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
self.bill=Listbox(self)
self.bill.pack()
for item in tb1:
co=" ".join(str(x) for x in item)
self.bill.insert(END, co)
app = SeaofBTCapp()
app.mainloop()
In the PageOne class You are reading the list tb1 only once, in __init__(). To get the changes in tb1 to be seen in the listbox you also have to update the listbox with the new altered list.
There is also an issue with the list. As it's defined in the global namespace your app will depend on this. I'd suggest you define it in the SeaofBTCapp() __init__() function and then you can access it through the controller object:
class SeaofBTCapp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.tb1 = [["Kofola", "0,5","30"]]
... etc ...
class StartPage(Frame):
def __init__(self, parent, controller):
self.controller = controller
... etc ...
def add(self):
self.controller.tb1.append(["Radegast", "0,5","30"])
... etc ...
And then add an update() method to the PageOne() class which updates the listbox and calls it from the add() method. I'm calling by way of controller method update_pageone(). See full example below:
from tkinter import *
class SeaofBTCapp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.tb1 = [["Kofola", "0,5","30"]] # Create instance variable tb1
container = 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 (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()
def update_pageone(self):
self.frames[PageOne].update() # Call update on PageOne
class StartPage(Frame):
def __init__(self, parent, controller):
self.controller = controller # Remember the controller
Frame.__init__(self,parent)
label = Label(self, text="Start Page")
label.pack(pady=10,padx=10)
button = Button(self, text="Visit Page 1",
command=lambda: self.controller.show_frame(PageOne))
button.pack()
button2 = Button(self, text="add", command=self.add)
button2.pack()
def add(self):
self.controller.tb1.append(["Radegast", "0,5","30"])
self.controller.update_pageone() # Asking controller for an update
class PageOne(Frame):
def __init__(self, parent, controller):
self.controller = controller # Remember the controller
Frame.__init__(self, parent)
label = Label(self, text="Page One!!!")
label.pack(pady=10,padx=10)
button1 = Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
self.bill = Listbox(self)
self.bill.pack()
for item in controller.tb1:
co = " ".join(str(x) for x in item)
self.bill.insert(END, co)
def update(self):
# Delete all from Listbox bill
self.bill.delete(0, 'end')
# Add revised table into Listbox bill
for item in self.controller.tb1:
co = " ".join(str(x) for x in item)
self.bill.insert(END, co)
app = SeaofBTCapp()
app.mainloop()

python StartPage instance has no attribute 's'

I am getting:
print (p.s) AttributeError: StartPage instance has no attribute 's'
here is my code:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.s = []
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
container = tk.Frame(self)
container.grid(row=0, column=0, sticky='we')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, images_result):
page_name = F.__name__
frame = F(parent=container, controller=self)
frame.grid(row=0, column=0, sticky='nsew')
self.frames[page_name] = frame
self.show_frame("StartPage")
def get_SP(self):
return self.frames["StartPage"]
def images(self, page_name):
self.s = [1, 2]
frame = self.frames[page_name]
frame.tkraise()
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.grid(row=0, column=0, sticky='snew')
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.image_btn = tk.Button(self, text="Images", command=lambda: controller.images("images_result"))
self.image_btn.grid(row=0, column=0, sticky='snew')
class images_result(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
p = self.controller.get_SP()
print (p.s)
app = SampleApp()
app.mainloop()
Edited:
I want the function images in SampleApp to update the attribute s of the SampleApp then I want to be able to access it from the class images_results.
I tried to use
print(self.controller.s)
but that gives me [] and by that it seems that the modification that has been done by the function images to the s attribute are not visible to the images_result.
The only class in your code which as an s attribute is SampleApp. The instance of this class is passed as the controller attribute for each page. Thus, to access the s attribute of the SampleApp object from within a page will be through the controller:
print(self.controller.s)
It's important to note that in the code you posted, the __init__ method of images_remote will only run when the app first starts up, so any values set by interacting with the other pages won't be printed by the __init__ function.

How to use a statusbar in many frames?

I managed to build a little GUI, where I can switch between frames related to the following question: Switch between frames in tkinter
I wanted to have a status bar on the Bottom of my GUI and it should stay on every frame! The status bar shows info about buttons when hovering over it!
Not hovered:
Hovered:
As you can see it works, BUT I can't manage to make the status bar update its values when another frame is raised, because I could only manage to create the status bar in one frame class...
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.title_font = font.Font(family='Helvetica', size=18,
weight="bold", slant="italic")
container = 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 (start_frame, cr_frame, db_frame):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.pack()
self.show_frame("start_frame")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
for frame in self.frames.values():
#frame.grid_remove()
frame.pack_forget()
frame = self.frames[page_name]
frame.pack()
if page_name == "start_frame":
frame.winfo_toplevel().geometry("545x200")
if page_name == "cr_frame":
frame.winfo_toplevel().geometry("600x200")
if page_name == "db_frame":
frame.winfo_toplevel().geometry("700x630")
class start_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
self.btn_cr = Button(self, text="Copyright Analyse", command=lambda: self.controller.show_frame("cr_frame"), width=40)
self.btn_cr.pack(side=LEFT, padx=15, pady=1, ipady=40)
self.btn_db = Button(self, text="Copyright Datenbank", command=lambda: self.controller.show_frame("db_frame"), width=40)
self.btn_db.pack(side=LEFT, pady=1, ipady=40)
###################Here is the statusbar defined + bindings and so on##########
self.lbl_status = Label(self.controller, text="...", border=1, relief=SUNKEN, anchor=W)
self.lbl_status.pack(side=BOTTOM, fill=X, anchor=W)
self.btn_cr.bind("<Enter>", lambda event: self.lbl_status.configure(text="Open copyright analysis window..."))
self.btn_cr.bind("<Leave>", self.leave_bindings)
self.btn_db.bind("<Enter>", lambda event: self.lbl_status.configure(text="Open copyright database..."))
self.btn_db.bind("<Leave>", self.leave_bindings)
class cr_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
Now, if I switch to page 2 (cr_frame), the status bar is still there because I attached it to the top-level controller (of all frames) but I can't edit it through the cr_frame class...
I don't know how to do it.
First, move the statusbar to the main app since it's part of the app and not part of a page:
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
...
container = Frame(self)
self.lbl_status = Label(self, text="", border=1, relief=SUNKEN, anchor=W)
container.pack(side="top", fill="both", expand=True)
self.lbl_status.pack(side="bottom", fill="x")
Next, add a method to the app for setting the status:
class SampleApp(Tk):
...
def set_status(self, string):
self.lbl_status.configure(text=string)
Finally, call that method whenever you need to change the status:
class cr_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
...
def something(self):
...
self.controller.set_status("Hello, world")
...

Shared Data between Frames Tkinter

I was hoping someone could tell me where I am going wrong. I would love to be able to access this shared_Data dictionary from any one of my classes, however I get the error "AttributeError: '_tkinter.tkapp' object has no attribute 'shared_Data'" when I run script. I am trying to implement what is described in this answer https://stackoverflow.com/a/33650527/7336464
import tkinter as tk
from tkinter import ttk
LARGE_FONT = ("Verdana", 20)
class APTInventoryapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default='barcode_scanner.ico')
tk.Tk.title(self, 'Inventory Scanning')
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 (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.shared_Data = {'String1': tk.StringVar(),
'String2' : tk.StringVar(),
'number1' : tk.IntVar()
}
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
upcOutputLabel = ttk.Label(self, text = self.controller.shared_Data['String1'].get(), font=LARGE_FONT)
upcOutputLabel.pack(pady=10,padx=10)
upcEntryField = ttk.Entry(self)
upcEntryField.focus()
upcEntryField.pack()
upcEnterButton = ttk.Button(self, text = 'Enter', command=print('Enter Clicked!'))
upcEnterButton.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
mgrOutputLabel = ttk.Label(self, text = '', font=LARGE_FONT)
mgrOutputLabel.pack(pady=10,padx=10)
mgrbackButton = ttk.Button(self,text = 'Back to home', command=lambda: controller.show_frame(StartPage))
mgrbackButton.pack()
app = APTInventoryapp()
app.mainloop()
You don't create self.shared_Data until after you create the pages, but you are using it while creating the pages. There appears to be other errors in the app, but that's why you're getting this specific error.

Categories

Resources