I have created 3 frames and each of these frame has their own daemons,to navigate among frames I am using the stack method.everything works fine but my problem is all daemons are running even though I did not open their respective frames,I know that I am loading the frames into stack that is reason all daemons started running. But I want those daemons to run only when their frame is opened,so is there any way i can do it. Example- Mainfile.py
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
from Page1 import PageOne
from Page2 import PageTwo
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, PageTwo):
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)
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()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
the above file is a main file now in that file I am importing two more frames which are having their own daemons, file-Page1
import threading
import tkinter as tk
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()
thread1 = threading.Thread(target=self.func)
thread1.daemon=True
thread1.start()
def func(self):
print("this is thread1")
file-Page2
import threading
import tkinter as tk
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", 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()
thread2 = threading.Thread(target=self.func)
thread2.daemon=True
thread2.start()
def func(self):
print("this is thread2")
After I run the whole program I can see both the daemons giving answers,but I want them to run only after their frame is opened,for example when frame PageOne is opened then its thread-threa1 should run the print the answer.
Is there anyway I can do that?
Note:this is just a simple example I am giving,but in real I am dealing with real-time hardware values,so by using threads and daemons,I can generate both UI and Processing stuff of hardware parallelly
The simple solution is to not create the frames until you need them. You can do that my modifying show_frame
to create the frames on demand.
First, you need to modify the code that initializes the UI to not create the frames. Instead, it can just create a mapping from a page name to the page class. You need to remove the loop that begins with for F in (StartPage, PageOne, PageTwo):
and replace it with something like this:
self.frames = {
"StartPage": StartPage,
"PageOne": PageOne,
"PageTwo": PageTwo,
}
Next, modify show_frame
to destroy any existing frame and then create the new frame. It should look something like this:
def show_frame(self, page_name):
# destroy the old frame
for child in container.winfo_children():
child.destroy()
# create the new frame
frame_class = self.frames[page_name]
frame = frame_class(parent=self.container, controller=self)
frame.pack(fill="both", expand=True)
Another solution would be to keep the original code, but only start the threads when the frame is shown. An example of how to run a function when the frame is shown can be seen in the accepted answer to this question: How would I make a method which is run every time a frame is shown in tkinter