I have a gui application that has several windows and buttons to go forward and backward. To do this I am using a controller and raising the frames to the top with each window change. Here is my code for the controller and a typical frame.
import Tkinter as tk # python
from tkFileDialog import askopenfilename, asksaveasfilename
from analyzer import Options
TITLE_FONT = ("Helvetica", 18, "bold")
class TennisProgram(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)
#Allow the window to be resized
# container.resizable(width=True, height=True)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#List of options bundles. One bundle for each video
self.options_bundle_list = {}
self.frames = {}
#Init empty array to hold the list of files
#Placed in the container so that all views can access it
self.files = []
for F in (UploadPage, AnalysisOptionsPage, FormAnalysisOptions, MatchAnalysisOptions, MatchAnalysisOptionsPage2, OutputPage, ProcessingPage):
page_name = F.__name__
frame = F(container, 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")
# Name the window
self.title("Tennis Analyzer")
self.show_frame("UploadPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class UploadPage(tk.Frame):
#TODO Add logic to remove a file path from the list
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.createView()
def createView(self):
label = tk.Label(self, text="Upload Videos for Analysis", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
#Listbox to display the list of files to be processed
self.path_list = tk.Listbox(self)
#Additional options allow listbox to expand as the window is resized
self.path_list.pack(fill=tk.BOTH ,expand=True)
for path in self.controller.files:
self.path_list.insert(tk.END, path)
add_vidoes_button = tk.Button(self, text="Add Videos",
command=self.choose_files)
continue_button = tk.Button(self, text="Continue",
command=lambda: self.controller.show_frame("AnalysisOptionsPage"))
add_vidoes_button.pack(side=tk.TOP)
continue_button.pack(side=tk.BOTTOM)
def choose_files_paths(self):
#don't want a full GUI, so keep the root window from appearing
#self.controller.withdraw()
#Get a file name from the user
filename = askopenfilename()
#Add it to the list of files to be processed
self.controller.files.append(filename)
self.path_list.insert(tk.END, filename)
The code that I write in init gets executed once and creates the view but I was wondering if it was possible to have a function which runs every time the frame got raised, similar to an onResume function in Android. I would want to do this in case some underlying data has changed like in this upload page. For example, what if an item is deleted from the array which populates the listview, I would want to be able to refresh it.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Tkinter has low level events such as <Visibility> and <Map> which should fire when the pages change. Unfortunately, these don’t work reliably on all platforms.
The simplest and most robust solution is to generate your own event. You can create and bind to a custom event by specifying the event inside << and >> (eg: <<ShowFrame>>).
First, change show_frame to send an event to a window when it is shown:
def show_frame(self, page_name):
...
frame.event_generate("<<ShowFrame>>")
Next, each page can bind to this event if it needs to be notified when it is made visible:
class UploadPage(tk.Frame):
def __init__(self, parent, controller):
...
self.bind("<<ShowFrame>>", self.on_show_frame)
def on_show_frame(self, event):
print("I am being shown...")
Method 2
It has passed a bit of time, but i found a solution to your problem (in case anyone needs this), that does not require some integrated function, but just the time library.
I made an easy to use function
def updatewindow(fps=60):
time.sleep(1/fps)
root.update()
#root.update_idletasks() # usually you don't need this
This is an example snippet that uses this function
from tkinter import *
import time
root=Tk()
root.title("Clock")
timelbl=Label(root,font=('helvetica',40,'bold'),fg='red',bg='black')
def updatewindow(fps=60):
time.sleep(1/fps)
root.update()
#root.update_idletasks() # usually you don't need this
timelbl.pack()
while True:
timelbl["text"]=time.strftime("%H:%M:%S")
updatewindow(fps=30)
You can run anything in the while, and it will work every frame.
Not the best solution, but a solution nontheless.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0