[Tkinter] Background inactivity timer when tkinter window is not active - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Background inactivity timer when tkinter window is not active (/thread-36941.html) |
Background inactivity timer when tkinter window is not active - DBox - Apr-14-2022 I'm having trouble getting a timer (loop or anything) running when the tkinter window is withdrawn or not on top of other windows. The functionality of this is like a small screen saver, start the program, have icon displayed, click show the full screen image appears, if triple click it disappears and goes back to system tray. All of that works, however the timer in the after method only works if the window is displayed, and I want it to work when it's not displayed so it can display it after a period of inactivity (10 secs for testing). How can I get the inactivity timer working when the tkinter window isn't displayed? from tkinter import * from ctypes import Structure, windll, c_uint, sizeof, byref import time import pystray from PIL import Image, ImageTk class LASTINPUTINFO(Structure): _fields_ = [ ('cbSize', c_uint), ('dwTime', c_uint), ] def get_idle_duration(): lastInputInfo = LASTINPUTINFO() lastInputInfo.cbSize = sizeof(lastInputInfo) windll.user32.GetLastInputInfo(byref(lastInputInfo)) millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime return millis / 1000.0 class FS_Display: def __init__(self): self.root = Tk() self.root.title("FS Display") self.root.bind("<Triple-1>", self.end_fullscreen) self.root.bind("<Escape>", self.end_fullscreen) self.root.protocol('WM_DELETE_WINDOW', self.end_fullscreen) self.end_fullscreen() def end_fullscreen(self, event=None): self.root.withdraw() self.image = Image.open("favicon.png") self.menu = (pystray.MenuItem('Quit', self.quit), pystray.MenuItem('Show', self.show_fullscreen)) self.icon = pystray.Icon("name", self.image, "FS Display", self.menu) self.icon.run() def show_fullscreen(self, event=None): self.root.attributes("-fullscreen", True) self.icon.stop() self.root.deiconify() def quit(self, event=None): self.icon.stop() self.root.destroy() def idle_check(self, event=None): GetLastInputInfo = int(get_idle_duration()) print(GetLastInputInfo) if GetLastInputInfo == 10: self.show_fullscreen() self.root.after(1000, self.idle_check) if __name__ == '__main__': fs = FS_Display() main_image = Image.open("D:black_background.png") photo = ImageTk.PhotoImage(main_image) Label(fs.root, image=photo).pack() fs.root.after(1000, fs.idle_check) fs.root.mainloop() RE: Background inactivity timer when tkinter window is not active - deanhystad - Apr-14-2022 This works fine for me. import tkinter as tk from ctypes import Structure, windll, c_uint, sizeof, byref class LASTINPUTINFO(Structure): _fields_ = [ ('cbSize', c_uint), ('dwTime', c_uint), ] def get_idle_duration(): lastInputInfo = LASTINPUTINFO() lastInputInfo.cbSize = sizeof(lastInputInfo) windll.user32.GetLastInputInfo(byref(lastInputInfo)) count = windll.kernel32.GetTickCount() return (count - lastInputInfo.dwTime) / 1000.0 class Window(tk.Tk): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.counter = tk.IntVar(self, 0) self.timer = tk.DoubleVar(self, 0.0) tk.Label(self, textvariable=self.counter, width=20).pack(padx=10, pady=10) tk.Button(self, text="Iconify", command=self.iconify).pack(padx=10, pady=10) self.update() def update(self): self.counter.set(self.counter.get() + 1) if get_idle_duration() > 10.0: print("Time to wake up!") self.deiconify() self.after(1000, self.update) Window().mainloop()It also works if the Iconify button calls withdraw() instead of iconify(). RE: Background inactivity timer when tkinter window is not active - DBox - Apr-14-2022 (Apr-14-2022, 08:00 PM)deanhystad Wrote: This works fine for me. Thank you for the sample. I guess it does work, however it appears that the pystray code is not allowing it to function properly. It blocks on icon.run() until icon.stop(). Thank you for the nudge in the right direction. RE: Background inactivity timer when tkinter window is not active - deanhystad - Apr-15-2022 From the pstray documentation. https://buildmedia.readthedocs.org/media/pdf/pystray/stable/pystray.pdf Quote:he call to pystray.Icon.run() is blocking, and it must be performed from the main thread of the application. You will not be able to do what you want with ".after()" which requires mainloop() to process the event. I think you need a to run the idle check in a separate thread. import tkinter as tk import time import threading from ctypes import Structure, windll, c_uint, sizeof, byref class LASTINPUTINFO(Structure): _fields_ = [ ('cbSize', c_uint), ('dwTime', c_uint), ] def get_idle_duration(): lastInputInfo = LASTINPUTINFO() lastInputInfo.cbSize = sizeof(lastInputInfo) windll.user32.GetLastInputInfo(byref(lastInputInfo)) count = windll.kernel32.GetTickCount() return (count - lastInputInfo.dwTime) / 1000.0 def run_blocking_task(): """Simulate icon.run()""" global running_blocking_task running_blocking_task = True while running_blocking_task: time.sleep(0.1) def stop_blocking_task(): """Simulate icon.stop()""" global running_blocking_task running_blocking_task = False class Window(tk.Tk): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.counter = tk.IntVar(self, 0) self.timer = tk.DoubleVar(self, 0.0) tk.Label(self, textvariable=self.counter, width=20).pack(padx=10, pady=10) tk.Button(self, text="Iconify", command=self.dock).pack(padx=10, pady=10) self.update() def dock(self): """This will block tkinter from running. Start thread to run idle check""" self.iconify() self.idle_thread = threading.Thread(target=self.idle_check) self.idle_thread.start() print("Time to go to bed!") run_blocking_task() print("Time to wake up!") self.deiconify() def idle_check(self): while True: if get_idle_duration() > 10.0: stop_blocking_task() break else: time.sleep(0.1) def update(self): self.counter.set(self.counter.get() + 1) self.after(1000, self.update) Window().mainloop()Running the idle checker in a different thread let's that code run in parallel with the dock, but it does stop tkinter.mainloop(). Notice that the counter in the window does not increment while the window is hidden. Is that a problem? RE: Background inactivity timer when tkinter window is not active - DBox - Apr-16-2022 (Apr-15-2022, 06:38 PM)deanhystad Wrote: Running the idle checker in a different thread let's that code run in parallel with the dock, but it does stop tkinter.mainloop(). Notice that the counter in the window does not increment while the window is hidden. Is that a problem? No, that's not a problem at all for what I'm trying to do. Thanks so much for this, I believe I can do what I need to now. |