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!
Related
I have this code to change the title of the frame. I have two pages here. In PageOne class, we get the title and by pressing the button, the title changes. And, we can use the Go to page two button to navigate to PageTwo. here also we can do the same.
Here is my question, how I can define only one function instead of using function add1() in PageOne, and function add2() in PageTwo. I mean I only define one function on PageOne and use the same function in PageTwo, and show the results in the frame2.
import tkinter as tk
class Data:
def __init__(self):
self.number1 = tk.StringVar()
self.number2 = tk.StringVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.minsize(700, 700)
container = tk.Frame(self)
container.pack()
self.data = Data()
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self.data)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.frames[PageOne].page_button.config(command=self.go_to_page_two)
self.show_frame(PageOne)
def go_to_page_two(self):
self.show_frame(PageTwo)
def show_frame(self, c):
frame = self.frames[c]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
entry1 = tk.Entry(self, textvariable=self.data.number1)
entry1.pack()
self.button1 = tk.Button(self, text="click", command=self.add1)
self.button1.pack()
self.frame1 = tk.LabelFrame(self, height=200, width=200, borderwidth=2)
self.frame1.pack()
self.page_button = tk.Button(self, text="Go to Page Two")
self.page_button.pack()
def add1(self):
self.frame1.config(text=str(self.data.number1.get()))
class PageTwo(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
entry2 = tk.Entry(self, textvariable=self.data.number2)
entry2.pack()
self.button2 = tk.Button(self, text="click", command=self.add2)
self.button2.pack()
self.frame2 = tk.LabelFrame(self, height=200, width=200, borderwidth=2)
self.frame2.pack()
def add2(self):
self.frame2.config(text=str(self.data.number2.get()))
app = SampleApp()
app.mainloop()
you can do in this way:
import tkinter as tk
class Data:
def __init__(self):
self.number = tk.StringVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.minsize(700, 700)
container = tk.Frame(self)
container.pack()
self.data = Data()
self.frames = {}
for F in (PageOne,):
frame = F(container, self.data)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
def show_frame(self, c):
frame = self.frames[c]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
entry1 = tk.Entry(self, textvariable=self.data.number)
entry1.pack()
self.button1 = tk.Button(self, text="click", command=lambda : self.add(self.frame1, data.number))
self.button1.pack()
self.frame1 = tk.LabelFrame(self, height=200, width=200, borderwidth=2)
self.frame1.pack()
self.button2 = tk.Button(self, text="click", command=lambda : self.add(self.frame2, data.number))
self.button2.pack()
self.frame2 = tk.LabelFrame(self, height=200, width=200, borderwidth=2)
self.frame2.pack()
def add(self, frame, data):
frame.config(text=str(self.data.number.get()))
app = SampleApp()
app.mainloop()
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
I have this code here and I want to pass values from one class (StartPage), to another (PageOne). I made a method in my controller "get_page" that let me access to page data among classes. So, I have an entry in StartPage and i want to pass its value to PageOne. So, the code runs, but in PageOne there is a print for debug. As soon as i start the program it automatically run all the code, almost like if PageOne is called even if I don't press the button. And the value I want to pass to the second page never gets passed. Can anyone help me out here please?
Thanks in advance!
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.some_input = tk.StringVar()
self.some_entry = tk.Entry(self, width=8)
self.some_entry.pack()
self.some_input = self.some_entry.get()
button1 = tk.Button(self, text='Next Page', command=lambda: controller.show_frame("PageOne"))
button1.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.some_entry.get()
print ('The value stored in StartPage entry = %s' % start_page.some_input)
label = tk.Label(self, text="This is page 1, the value stored is" + str(value), 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()
You need to find a way to notify PageOne to update the label whenever it is shown by show_frame(), one of the way is via virtual event using event_generate() function:
class SampleApp(tk.Tk):
...
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
# notify frame that it is raised via virtual event
frame.event_generate("<<Raised>>")
...
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
# save a reference of 'StartPage'
self.start_page = self.controller.get_page('StartPage')
# use instance variable 'self.label' instead of local variable 'label'
self.label = tk.Label(self, font=controller.title_font)
self.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()
# bind the virtual event
self.bind("<<Raised>>", self.on_raised)
def on_raised(self, event):
self.label.config(text=f"This is page 1, the value stored is '{self.start_page.some_entry.get()}'")
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.
I have a container with a set of frames, that provide me with two pages (main page and an info page.) Buttons are used to navigate the pages.
All is working.
I have defined a function to remove folders on the OS. (shutil.rmtree) This function should be called by my button 2 on the main page, but it falls over and is not working.
import tkinter as tk
import shutil
from tkinter import *
TITLE_FONT = ("Helvetica", 18, "bold")
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=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (MainPage, InfoPage,):
frame = F(container, self)
self.frames[F] = 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(MainPage)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class MainPage(tk.Frame):
def callback(self):
shutil.rmtree('C:\Test', ignore_errors=False)
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
photo = PhotoImage(file='C:\Logo.gif')
label = Label(self, image=photo)
label.Image = photo
label.pack(side=TOP)
button1 = tk.Button(self, text="Info!",
command=lambda: controller.show_frame(InfoPage))
button1.pack(side=LEFT, padx=5, pady=5)
button2 = tk.Button(self, text="Clean!", command=callback)
button2.pack(side=RIGHT, padx=5, pady=5)
class InfoPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Info Page", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Back",
command=lambda: controller.show_frame(MainPage))
button.pack()
if __name__ == "__main__":
app = App()
app.mainloop()