How to update listbox when I change view in Tkinter? - python

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()

Related

Cannot call show_frame function in tkinter outise of its class

With the code below (that I took from sentdex) I am trying to raise PageOne window when the correct password("123") is inserted in the first page. However, I get the error: TypeError: show_frame() missing 1 required positional argument: 'cont'. Isn't PageOne the argument ? Why is not working ? Could you also please explain what "controller" variable does ?
Thank you very much in advance.
import tkinter as tk
import tkinter as tk
from PIL import Image,ImageTk
LARGE_FONT= ("Verdana", 12)
class Root(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 (StartPage, PageOne, PageTwo):
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(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.entry=tk.Entry(self, width = 35)
self.entry.insert(0, 'Enter password')
self.entry.config(fg = "grey")
# entry.bind('<FocusIn>', self.EntryFieldClicked)
# entry.bind("<Return>", (lambda event: self.SubmitPass()))
self.entry.place(relx=.5, rely=.3,anchor = tk.CENTER)
button=tk.Button( self,text="Show Password", width = 20,command = self.ShowPass).place(relx=.5, rely=.4,anchor = tk.CENTER)
button=tk.Button(self,text="Submit",width = 20, command=self.SubmitPass).place(relx=.5, rely=.5,anchor = tk.CENTER)
self.entry.config(fg = "grey")
self.entry.bind('<FocusIn>', self.EntryFieldClicked)
self.entry.bind("<Return>", (lambda event: self.SubmitPass ))
label=tk.Label(self,text="Log in to continue")
label.pack()
button = tk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
def EntryFieldClicked(self,event):
if self.entry.get() == 'Enter password':
self.entry.delete(0, tk.END)
self.entry.insert(0, '')
self.entry.config(fg = 'black', show = "*")
def ShowPass(self):
self.entry.config(fg = 'black', show = "")
def SubmitPass(self):
global Password
Password = self.entry.get()
if Password == "123":
Root.show_frame(PageOne)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
self.controller = controller
button1 = tk.Button(self, text="Back to Home",
command=lambda: self.controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = Root()
app.mainloop()
Blockquote
The problem is this line:
Root.show_frame(PageOne)
You are calling the method on the class rather than the instance, so the first argument is passes to the self parameter.
Your class needs to keep a reference to the controller, so that you can call the method via the controller.
class StartPage(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def SubmitPass(self):
global Password
Password = self.entry.get()
if Password == "123":
self.controller.show_frame(PageOne)

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

(tkinter) Why aren't my variables being displayed using labels?

I'm trying to write a program which takes the users information, like their name and stuff, on one page, and then displays these entries on another. I'm using Tkinter and I can't get their entries to display on the other page. Here's the program:
import tkinter as tk
from tkinter import ttk
#PROFILE VARS
FirstName = ('none')
#INITIALIZING
class MegaQuiz(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "THE MEGA POP QUIZ")
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 (ProfilePage, MainPage):
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("ProfilePage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
#PROFILE PAGE
class ProfilePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
global FirstName
#Profile Title
profile_title = tk.Label(self, text="Create A Profile Yo")
profile_title.grid(column=0, row=2)
#FIRST NAME
Q1_title = tk.Label(self, text="First Name:")
Q1_title.grid(column=0, row=1)
FirstNameEntry = tk.Entry(self)
FirstNameEntry.grid(column=2, row=4)
FirstName = str(FirstNameEntry.get())
#NEXT BUTTON
Button1 = tk.Button(self, text="NEXT",
command = lambda: controller.show_frame("MainPage"))
Button1.grid(column=10, row=10)
#MAIN MENU PAGE
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#Play BUTTON
PlayBTN = tk.Button(self, text="PLAY", width=40)
PlayBTN.grid(column=0, row=20, sticky="nesw")
#ProfileDisplay
FirstNameDis = tk.Label(self, text=('FirstName: ' + FirstName))
FirstNameDis.grid(column=0, row=0, sticky="w")
#RUNNING PROGRAM
app = MegaQuiz()
app.mainloop()
The problem is that it displays the "FirstName: ", but doesn't display the variables FirstName, just blankness, help.
The quickest way to fix this is to move your FirstName variable to the MegaQuiz as a class attribute and then use it from there. We can change your NEXT button command to call a class method and then from that method update the FirstName variable and then also set the FirstNameDis label text from that same method.
First move the FirstName variable into your MegaQuiz class by putting this line in the __init__ section.
self.FirstName = 'none'
Then change this:
#NEXT BUTTON
Button1 = tk.Button(self, text="NEXT", command = lambda: controller.show_frame("MainPage"))
To this:
#NEXT BUTTON
Button1 = tk.Button(self, text="NEXT", command=self.next_button)
Button1.grid(column=10, row=10)
def next_button(self):
self.controller.FirstName = self.FirstNameEntry.get()
self.controller.frames["MainPage"].FirstNameDis.config(text='FirstName: ' + self.controller.FirstName)
self.controller.show_frame("MainPage")
In your MainPage change this:
self.FirstNameDis = tk.Label(self, text='FirstName: ' + FirstName)
To this:
self.FirstNameDis = tk.Label(self, text='FirstName: ' + self.controller.FirstName)
That should be all you need to fix this.
I did notice a few PEP8 issues so here is your code rewritten to provide a fix to your problem and also to rewrite some things to better follow the PEP8 guidelines.
Not sure why you are adding () to some of your string variables but it is not needed.
variable and class attribute names should be all lower case with underscores to between words.
You do not need to provide a variable name for everything. If you have a button or a label you know you will not be updating later then you can write them without the variable names.
Code:
import tkinter as tk
class MegaQuiz(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self)
self.title("THE MEGA POP QUIZ")
self.first_name = 'none'
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 (ProfilePage, MainPage):
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("ProfilePage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class ProfilePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tk.Label(self, text="Create A Profile Yo").grid(column=0, row=2)
tk.Label(self, text="First Name: ").grid(column=0, row=1)
self.fn_entry = tk.Entry(self)
self.fn_entry.grid(column=2, row=4)
tk.Button(self, text="NEXT", command=self.next_button).grid(column=10, row=10)
def next_button(self):
self.controller.first_name = self.fn_entry.get()
self.controller.frames["MainPage"].fn_dis.config(text='FirstName: {}'.format(self.controller.first_name))
self.controller.show_frame("MainPage")
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tk.Button(self, text="PLAY", width=40).grid(column=0, row=20, sticky="nesw")
self.fn_dis = tk.Label(self)
self.fn_dis.grid(column=0, row=0, sticky="w")
app = MegaQuiz()
app.mainloop()

Frame Update Tkinter

So I have read a few posts on here and have tried them all. The current method I have attempted is storing the data in the controller:
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.app_data = {"building": tk.StringVar()}
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,PageTwo):
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()
I then have my PageOne receive input from user as to what building they want:
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#buidling
bldg_label = ttk.Label(self, text="Building Name (ie. ACAD):", font=LARGE_FONT)
bldg_label.grid(row=0, column=0, padx=2, pady = 10)
self.bldg_entry =ttk.Entry(self,width = 6, textvariable = self.controller.app_data["building"])
self.bldg_entry.grid(row=0, column=1, padx=2, pady=10)
button1 = ttk.Button(self, text="next", command = lambda: controller.show_frame(PageTwo))
button1.grid(row = 1, column = 0)
Lastly I want to display the building they typed into the entry box into a label like so:
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
bldg = self.controller.app_data["building"].get()
bldg_label = ttk.Label(self, text = "Building: " + str(bldg), font=LARGE_FONT)
bldg_label.grid(row = 0, column =0)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.grid(row = 1, column =0)
I know there needs to be some sort of updating but I just can't seem to get the right structure to make this work. The label prints: Building: with nothing in it. My question is how can I get the data that is stored in the dictionary to update the building label in the SecondPage? Any help would be greatly appreciated!

Checkbutton widget in Tkinter (OOP)

The following is an excerpt of a my first python project I am currently working on.
I am able to add the check button into the GUI, but it is practically useless because I can't find out how I can get the value from the checkbutton.
import tkinter as tk
from tkinter import *
class base(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
#tk.Tk.iconbitmap(self, "iconz.ico")
tk.Tk.wm_title(self, "Mandelbrot Renderer")
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, MainPage):
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 MainPage(tk.Frame):
def getcheckvalue(self):
print (self.mvar.get())
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Graph Page!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = ttk.Button(self, text="Re-Render",
command=self.getcheckvalue)
button2.pack()
mvar = IntVar()
self.cbutton = ttk.Checkbutton(self, text="shadow",onvalue=1, offvalue=0, variable=mvar)
self.cbutton.pack()
app = base()
app.geometry ("800x600")
app.mainloop()
mvar = IntVar()
In this line you are creating a local mvar. To access it from outside of your __init__ method, you need to make it class variable by adding self. prefix.
self.mvar = IntVar()
self.cbutton = ttk.Checkbutton(..., variable=self.mvar)

Categories

Resources