Updating tkinter label in a frame object - python

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

Related

tkinter passing data among classes

I was reading this tutorial of StackOverflow on how to get variables among classes, but I'm not able to make it work. Basically what i want to do is to pass an entry value from StartPage to PageOne where i need it. I also tried "the wrong way" using global variables, but I need an int and I can't covert the string entry to an int. Down below there's my code, can you help me please?
import tkinter as tk # python 3
from tkinter import font as tkfont
from typing_extensions import IntVar # python 3
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")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
def get_page(self, classname):
'''Returns an instance of a page given it's class name as a string'''
for page in self.frames.values():
if str(page.__class__.__name__) == classname:
return page
return None
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Start Page: insert value", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.valore = tk.IntVar()
entry = tk.Entry(self, textvariable=self.valore).pack()
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
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)
self.controller = controller
start_page = self.controller.get_page("StartPage")
value = start_page.entry.get()
print ('The value stored in StartPage entry = %s' % value)
label = tk.Label(self, text="This is page 1", 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()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

Trying to avoid combining .pack() and .grid() using tkinter and struggling. Which option is better and why?

I have been working on a developing a GUI using Python, Tkinter and Pillow (simple UI for a work application I am working on). I consistently run into an issue about the geometry manager running into issues because you 'cannot use geometry manager grid inside . which already has slaves managed by pack'.
This issues only happens when I add my class page_Roster to the application. I am not sure what I am doing run, but I suspect I must have goofed something up with the __init__ declaration or instantiation. Any help appreciated, but certainly chief among my concerns is figuring out how to work with which geometry manager in tkinter and when/why!
import tkinter as tk
from tkinter import font as tkfont
from tkinter import *
import importlib
from PIL import *
from PIL import Image
from PIL import ImageTk
class BellBankLMS(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=14, weight="bold", slant="italic")
self.title('Bell Bank: Learning Management Software')
self.geometry("450x450")
self.resizable(0, 0)
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 (page_Menu, page_Training, page_Quizzes, page_Mgmt, page_Roster):
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("page_Menu")
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
# frame.pack(side="top", fill="both", expand=True)
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class page_Menu(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
### This code is not working currently // It displays Bell Bank logo
# BellLogo = Image.open('bell1.png')
# BellLogo = BellLogo.resize((85, 85), Image.ANTIALIAS)
# renderedLogo = ImageTk.PhotoImage(BellLogo)
# LogoLabel = tk.Label(image=renderedLogo)
# LogoLabel.pack()
label = tk.Label(self, text="Bell Bank: Learning Management Software", font=controller.title_font, background="blue", foreground="white")
label.pack(side="top")
button1 = tk.Button(self, text="Training",
command=lambda: controller.show_frame("page_Training"))
button2 = tk.Button(self, text="Quizzes",
command=lambda: controller.show_frame("page_Quizzes"))
button3 = tk.Button(self, text="Management",
command=lambda: controller.show_frame("page_Mgmt"))
button1.pack(expand=1)
button2.pack(expand=1)
button3.pack(expand=1)
class page_Training(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Training To-Do, Scheduling, etc.", foreground="blue", background="lightgray")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("page_Menu"))
button.pack()
class page_Quizzes(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Quiz Scores, Performance, etc.", foreground="blue", background="lightgray")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("page_Menu"))
button.pack()
class page_Mgmt(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Trainer Admin: Course, Roster and Training Mgmt", foreground="blue", background="lightgray")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("page_Menu"))
button1 = tk.Button(self, text="Training Classes/Rosters",
command=lambda: controller.show_frame("page_Roster"))
button.pack()
button1.pack()
class page_Roster(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tasks=None
super().__init__()
if not tasks:
self.tasks = []
else:
self.tasks = tasks
trainee1 = tk.Label(self, text="---Add Trainee Here---", bg="lightgrey", fg="blue", pady=10)
self.tasks.append(trainee1)
for task in self.tasks:
task.pack(side="top", fill="x")
self.task_create = tk.Text(self, height=3, bg="white", fg="black")
self.task_create.pack(side="bottom", fill="x")
self.task_create.focus_set()
self.bind("<Return>", self.add_task)
self.color_schemes = [{"bg": "lightgrey", "fg": "blue"}, {"bg": "grey", "fg": "white"}]
def add_task(self, event=None):
task_text = self.task_create.get(1.0,END).strip()
if len(task_text) > 0:
new_task = tk.Label(self, text=task_text, pady=10)
_, task_style_choice = divmod(len(self.tasks), 2)
my_scheme_choice = self.color_schemes[task_style_choice]
new_task_configure(bg=my_scheme_choice["bg"])
new_task_configure(fg=my_scheme_choice["fg"])
new_task.pack(side="top", fill="x")
self.tasks.append(new_task)
self.task_create.delete(1.0, END)
if __name__ == "__main__":
app = BellBankLMS()
app.mainloop()
Consider this code:
class page_Roster(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tasks=None
super().__init__()
Notice that you are first doing tk.Frame.__init__(self, parent), which calls the __init__ of the superclass. A couple lines later you do super().__init__() which also calls the __init__ of the superclass. Only, in this second attempt you are not passing parent to the function. Because of that, this widget is a child of the root window.
You need to do one of the following:
remove the line super().__init__(), or
remove the line tk.Frame.__init__(self, parent) and change the other line to super().__init__(parent).

Tkinter switching frame with conditions

I use following code:
Switch between two frames in tkinter
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
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")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
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)
self.giris = tk.Entry(self)
self.giris.pack()
self.yazi = tk.Label(self, text="Buraya GİRİLEN VERİ gelecek.")
self.yazi.pack()
button2 = tk.Button(self, text="ae",
command=lambda: [self.alinanmetin(), controller.show_frame("PageOne")])
button2.pack()
def alinanmetin(self):
il = self.giris.get()
self.yazi.config(text="Girdiğiniz il: %s" % il)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", 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()
if __name__ == "__main__":
app = SampleApp()
app.geometry("600x400")
app.mainloop()
I want to change my frame according to the value I get from the entry.
if(il =="ali") i want change my frame and
if( il != "ali") i dont want do anything.
I am currently running two functions at the same time.How can i change frame after check value.

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

Why are my widgets not showing up on the 3rd screen

When you select 2 duelist on the second screen the program shows a 3rd screen that should display 2 labels and 2 entry widgets for the user to enter the names of the players. But I can't seem to figure out why the widgets are not showing up. The section of code that involves this issue is the the block for the class TwoPlayer. Thank you!
import tkinter as tk
largeFont = ("Veranda", 18)
field1 = 'Duelist 1', 'Duelist 2'
names = []
class Yugioh_backEnd(tk.Tk):
#set default initializion
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "YuGiOh Duel Calculator")
#containers
container = tk.Frame(self)
#set pack method for container
container.pack(side="top", fill="both", expand=True)
#set grid method for container
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#selects which frame to show
self.frames = {}
for F in (StartPage, NumPlayers, TwoPlayer):
frame = F(container, self)
self.frames[F]=frame
frame.grid(row=0, column=0, sticky="nsew")
#show Frame
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)
#greet the user
greeting = tk.Label(self, text = "Welcome to\n YuGiOh Duel Calculator!", font = largeFont)
greeting.pack(pady=(10,40),padx=30)
#Enter the next window
lets_duel = tk.Button(self, text="Lets Duel!!!", command=lambda: controller.show_frame(NumPlayers))
lets_duel.pack(pady=(0,30),padx=30)
class NumPlayers(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#prompt for players quantity
prompt1 = tk.Label(self, text = "How many duelist?", font = largeFont)
prompt1.pack(pady=(10,40),padx=30)
#Number of players
twoPlayers = tk.Button(self, text = "2 Duelists", command=lambda: controller.show_frame(TwoPlayer))
return1 = tk.Button(self, text="Return Home", command=lambda: controller.show_frame(StartPage))
#Add buttons to frame
return1.pack(pady=(0,30),padx=30)
twoPlayers.pack(pady=(0,10),padx=30)
#Two player mode
class TwoPlayer(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def makeform(field1):
for field in field1:
row = tk.Frame()
lab = tk.Label(row, width=15, text=field, anchor='w')
ent = tk.Entry(row)
row.pack(side="top", padx=5, pady=5)
lab.pack(side="left")
ent.pack(side="right")
names.append((field, ent))
return names
if __name__ == ("__init__"):
ents = makeform(field1)
b1 = tk.Button(text='Show',
command=lambda: controller.show_frame(StartPage))
b1.pack(padx=5, pady=5)
app = Yugioh_backEnd()
app.mainloop()

Categories

Resources