I'm having an issue where nothing is being placed in the frame and I'm not sure why as the template works when I tested that and can't see where I have done anything different other than use different widgets
Template used: https://www.semicolonworld.com/question/42826/switch-between-two-frames-in-tkinter
This is my code with most of the items removed other than a couple of examples
import tkinter as tk
from tkinter import *
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# 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=False)
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
def check_user_login():
#code removed
return None
#create a canvas to place images on
canvas = tk.Canvas(self, bg = "#5c5c5c",height = 400,width = 800,bd = 0,highlightthickness = 0,relief = "flat")
canvas.place(x = 0, y = 0)
#login button
button = tk.Button(self, text="Go to the start page",command=lambda: controller.show_frame("PageOne"))
button.place(x = 356, y = 290,width = 89,height = 29)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#create a canvas to place images on
canvas = Canvas(self,bg = "#5c5c5c",height = 400,width = 800,bd = 0,highlightthickness = 0,relief = "flat")
canvas.place(x = 0, y = 0)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = App()
app.geometry("800x400")
# app.configure(bg = "#5c5c5c")
app.resizable(False, False)
app.mainloop()
Any help would be massively appreciated I've been stuck on this for a while
Related
I'm designing a GUI with Tkinter. It has many frames(pages) that by pressing a button in one frame, that frame is destroyed and next frame is displayed. Each of the buttons has variable images, so I need a function that rotates the button image of each page being displayed.
I wrote the following code, the Address of the photos changes (self.Address in (def counter) of Pagestart class) but I think button.config cannot update images of button!!!Why???
(For Explain: countercounter function in showframe function of main class is responsible for updating of counter function in pagestart.)
the output of this code display a frame with one button that its image is constant and it can't update.
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import time
import os
import subprocess as sp
import signal
global counter0, counter1
counter0=0
counter1=0
class Project(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.configure(background="#000000")
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.container=container
self.frames = {}
for F in ( Pagestart, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Pagestart)
def show_frame(self, cont):
self.sw = 1000
self.sh = 1800
self.cont=cont
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[cont]
frame.configure(background="#000000")
frame.grid()
frame.winfo_toplevel().geometry('%dx%d+%d+%d' % (self.sw,self.sh,0,0))
A=Pagestart(parent=self.container, controller=self)
self.Pagestart=Pagestart
B=A.button
def countercounter(B):
def count1():
global counter0, counter1
A.counter()
if (self.cont==Pagestart):
B.after(100,count1)
count1()
countercounter(B)
def twoside(self, inputaddress, startframe, stopframe):
self.input = inputaddress
self.startframe = startframe
self.stopframe = stopframe
global counter0, counter1
def count():
global counter0, counter1
if (counter1==1):
counter0 -=1
if (counter1==0):
counter0 += 1
self.Address=('%s%s' % (str(self.input),str(counter0))+".jpg")
if (counter0==self.stopframe):
counter1=1
if (counter0==self.startframe):
counter1=0
count()
def sendAddress(self):
return self.Address
class Pagestart(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageOne))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference0/"
self.controller.twoside(self.inputaddress, 0, 138)
self.Address = self.controller.sendAddress()
self.photo = Image.open(self.Address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image=self.photo
self.button.config(image=self.photo)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(Pagestart))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference1/"
self.controller.twoside(self.inputaddress, 0, 138)
self.Address = self.controller.sendAddress()
self.photo = Image.open(self.Address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image=self.photo
self.button.config(image=self.photo)
if __name__ == "__main__":
app = Project()
app.mainloop()
You don't destroy frame but you only hide it using grid_forget()/grid_remove().
Don't create new instance of Pagestart because you already have old instance of Pagestart which is displayed with
frame = self.frames[cont]
frame.grid()
and you should change image in this instance using ie.
frame.counter()
Working code:
I use self.counter and self.animation_direction instead of global variables counter0, counter1.
I don't use nested functions because it is less readable.
I use change_image() to change image every 100ms
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class Project(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.counter = 1
self.animation_direction = 1 # it will add `+1` to self.counter
self.sw = 1000
self.sh = 1800
container = tk.Frame(self)
container.configure(background="#000000")
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.container = container
self.frames = {}
for F in ( PageStart, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(PageStart)
def show_frame(self, cont):
self.cont = cont
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[cont]
frame.configure(background="#000000")
frame.grid()
frame.winfo_toplevel().geometry('%dx%d+%d+%d' % (self.sw,self.sh,0,0))
#frame.counter()
self.change_image()
def twoside(self, inputaddress, startframe, stopframe):
self.input = inputaddress
self.startframe = startframe
self.stopframe = stopframe
self.counter += self.animation_direction
self.address = '%s%s.jpg' % (self.input, self.counter)
if self.counter == self.stopframe:
self.animation_direction = -self.animation_direction
if self.counter == self.startframe:
self.animation_direction = -self.animation_direction
def get_address(self):
return self.address
def change_image(self):
if self.cont == PageStart:
self.frames[self.cont].counter()
self.after(100, self.change_image)
class PageStart(tk.Frame): # PEP8: UpperCaseNames for classes
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageOne))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference0/"
self.controller.twoside(self.inputaddress, 0, 138)
self.address = self.controller.get_address() # PEP8: lower_case_names for functions/methods and variables
self.photo = Image.open(self.address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image = self.photo
self.button.config(image=self.photo)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.ButtonStyle = ttk.Style()
self.ButtonStyle.configure("Tabedstart.TButton", background="#000000", borderwidth=0)
self.ButtonStyle.map("Tabedstart.TButton", background=[('selected', "#000000")])
self.button = ttk.Button(self, style="Tabedstart.TButton", command=lambda: controller.show_frame(PageStart))
self.button.pack(pady=320)
self.counter()
def counter(self):
self.inputaddress = "/home/pi/Documents/Reference1/"
self.controller.twoside(self.inputaddress, 0, 138)
self.address = self.controller.get_address()
self.photo = Image.open(self.address)
self.photo = ImageTk.PhotoImage(self.photo)
self.button.image = self.photo
self.button.config(image=self.photo)
if __name__ == "__main__":
app = Project()
app.mainloop()
Unable to change change frames after a delay
How can I use the showFrames method from logoPage class in the code provided below?
class temp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "SOC_Workups")
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 (logoPage, mainPage):
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.showFrame("logoPage")
def showFrame(self, page):
frame = self.frames[page]
frame.tkraise()
class logoPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#label = tk.Label(self, text = "Start Page", font = LARGE_FONT)
#label.pack(pady = 10, padx = 10)
canvas = tk.Canvas(self, height=500, width=1000)
canvas.pack()
logo = tk.PhotoImage(file='logo1.png')
background_label = tk.Label(self, image=logo)
background_label.image = logo
background_label.place(relwidth=1, relheight=1)
#parent.showFrame("mainPage")
class mainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
canvas = tk.Canvas(self, height=500, width=1000)
canvas.pack()
app = temp()
app.mainloop()
The app should start with logoPage and then changes to mainPage after 10 secs. I am unable to call the showFrame method from logoPage class.
You have access to showPage() using controller
controller.showFrame("mainPage")
To run with delay you can use root.after(millisecond, callback). In your code it will need lambda to create callback which runs function with arguments`
parent.after(10000, lambda:controller.showFrame("mainPage"))
EDIT: As Bryan Oakley said in comment you can run it without lambda
parent.after(10000, controller.showFrame, "mainPage")
Second argument in after() is callback - function's name without () - and all its arguments are as next arguments in after()
I changed it in code below too.
Full working code:
There is good rule in PEP 8 -- Style Guide for Python Code which suggests to use CamelCaseNames for classes - ie. LogoPage, MainPage, Temp similar to Button, Frame, Button, etc. it helps to recognize class in code. And some tools use speciala color for classes - see colors in code below.
import tkinter as tk
class Temp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "SOC_Workups")
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 (LogoPage, MainPage):
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")
print(self.frames)
self.showFrame("LogoPage")
def showFrame(self, page):
frame = self.frames[page]
frame.tkraise()
class LogoPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#label = tk.Label(self, text = "Start Page", font = LARGE_FONT)
#label.pack(pady = 10, padx = 10)
canvas = tk.Canvas(self, height=500, width=1000)
canvas.pack()
logo = tk.PhotoImage(file='logo1.png')
background_label = tk.Label(self, image=logo)
background_label.image = logo
background_label.place(relwidth=1, relheight=1)
#parent.showFrame("mainPage")
#parent.after(10000, lambda:controller.showFrame("MainPage"))
parent.after(10000, controller.showFrame, "MainPage")
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
canvas = tk.Canvas(self, height=500, width=1000)
canvas.pack()
app = Temp()
app.mainloop()
I want to create a simple GUI for a constant data that comes through Serial. I decided to use tkinter. The value reading is updated and should be shown in a label. I created separate classes for the container and the other pages. I defined the container 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.frames={}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
frame.UpdateMe()
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
And the page showing the label:
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self,parent)
global reading
self.label1text = StringVar()
self.label1 = Label(self, textvariable = label1text)
self.label1.pack()
button1 = Button (self, text = "Show Start Page", command = lambda: controller.show_frame(StartPage))
button1.pack()
self.label1text.set(reading)
def UpdateMe(self):
global reading
self.lable1text.set(reading)
Now, to initialize the GUI:
root = Gui()
root.mainloop()
However, since mainloop() is blocking, any argument coming after that wouldn't be executed; I could get around that with update and update_idletasks. However, I still don't know how I could call the function UpdateMe() inside PageOne()when I only created an instantiation of the Gui(). Is there a way for me to solve this or remediate my understanding of classes and object programming?
Since you cannot create StringVar without initializing Tk() (for your case, it is Gui()), so you need to create the reading variable inside Gui() and PageOne.label1 uses it as its textvariable. Below is an example based on your code:
from tkinter import *
from random import randint
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)
container.grid_columnconfigure(0, weight=1)
self.reading = StringVar() # create the StringVar for PageOne
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames[F] = frame
self.show_frame(StartPage)
def show_frame(self, cont):
self.frames[cont].tkraise()
class StartPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
button1 = Button (self, text="Show Page 1", command=lambda: controller.show_frame(PageOne))
button1.pack(fill="both", expand=True)
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.label1 = Label(self, textvariable=controller.reading) # refer to Gui.reading StringVar
self.label1.pack(fill='x')
button1 = Button (self, text="Show Start Page", command=lambda: controller.show_frame(StartPage))
button1.pack(fill='x')
# use .after() to simulate the update of reading variable periodically
def update_reading():
app.reading.set(randint(0, 10000))
print('reading:', app.reading.get())
app.after(1000, update_reading)
app = Gui()
update_reading() # start the simulation task of updating reading variable
app.mainloop()
Note that I have created a function update_reading() to simulate the update of reading variable periodically using after() function.
How do I change the geometry of the window and label when I have multiple frames?
Without frames, my code would be:
nGui = Tk()
nGui.geometry("500x500")
But I'm unsure of what 'nGui' is in below code (my whole code as requested). Therefore when this is run it comes up as a very small window. I think it might be 'tk.Tk' but when i tried to edit it, it just made a new window.
class DietBuddy(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# 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, Diet_Finder):
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="HazaTea Productions", font=TITLE_FONT)
label.place(relx=.5, rely=.5, anchor="center")
time.sleep(2)
button = tk.Button(self, text="Continue",
command=lambda: controller.show_frame("PageOne"))
button.place(relx=.5, rely=.6, anchor="center")
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=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Find Diet",
command=lambda: controller.show_frame("Diet_Finder"))
button.pack()
class Diet_Finder(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Diet Finder", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Find my Diet!",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = DietBuddy()
app.mainloop()
Thank you in advance - if this is in the wrong place, please have mercy and tell me I'm new to this site.
DietBuddy class subclassed Tk. So call geometry method against the DietBuddy instance:
if __name__ == "__main__":
app = DietBuddy()
app.geometry('500x500') # <---
app.mainloop()
I need some help updating frames. I did some research (update() and update_idletasks()) but so far nothing has worked. I could be implementing these methods incorrectly. This is what i have so far...
import tkinter as tk
LARGE_FONT = ("Verdana", 12)
class controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
self.minsize(width=300, height=300)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {} # Store all the frames
for F in (StartPage, CustomerLocation, CustomerPickDate):
frame = F(container, self) # F is the classes
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage) # Make Start Page on the top
def show_frame(self, cont): # Used to bring the given frame to the top/ show frame
frame = self.frames[cont] # Access the dic of all the frames
frame.tkraise()
def get_page(self, page_class): # Get access to the page and its attributes
return self.frames[page_class]
def combine_funcs(self, *funcs): # Run multi funcs at one time (attach to a button!)
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
return combined_func
def updateFrame(self,frame):
selectedFrame = self.frames[frame]
selectedFrame.update_idletasks()
#frame.update_idletasks(self)
print("hit")
class CustomerLocation(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.myparent = parent
self.controller = controller
self.configure(background='#ED7D3B')
# ________________________________________________________________
# self.variableLocation is what I want in the next frame
# ________________________________________________________________
self.variableLocation = tk.StringVar(self)
self.variableLocation.set("Select")
alist = ["Blackwood", "Camden", "Philadelphia"]
locationOptionMenu = tk.OptionMenu(self, self.variableLocation, *alist)
locationOptionMenu.pack()
#print(self.variableLocation.get())
nextButton = tk.Button(self, text="Next",
command=lambda: controller.combine_funcs(controller.updateFrame(CustomerPickDate),
controller.show_frame(CustomerPickDate)
))
nextButton.configure(highlightbackground='#ED7D3B')
nextButton.pack()
backButton = tk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
backButton.configure(highlightbackground='#ED7D3B')
backButton.pack()
At this point self.variableLocation should be one of the alist variables. I used the controller.get_page(FRAME) to grab that value. I want to use this value as a label on the next frame.
class CustomerPickDate(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller # Help Access the controller + its methods
self.CustomerLocation = self.controller.get_page(CustomerLocation) # Access to Frame CustomerLocation
self.variableLocation = self.CustomerLocation.variableLocation
print(self.variableLocation.get())
label = tk.Label(self, text="Select Date", font=LARGE_FONT)
label.pack(pady=10, padx=10)
# _______________________________________________________
# I want the self.variableLocation from the pervious frame
# to be here!___________________________________________
self.label2 = tk.Label(self, text="TEST %s" % self.variableLocation.get()) # NEED FIXING/ ABLE TO UPDATE
self.label2.pack()
self.label2.update()
NextButton = tk.Button(self, text="Next")
NextButton.pack()
BackButton = tk.Button(self, text="Back",
command=lambda: controller.show_frame(CustomerLocation))
BackButton.pack()
I am very new to Tkinter, so any feed back would be great!