Enable multithreading/processes in tkinter GUI - python

My tkinter application has a function which has a time.sleep() function in it so anytime I run the function that has the time.sleep() function, the whole application freezes until seconds specified in the time.sleep() function has elapsed. This can be a very bad User experience and I need to fix this.
The function that has the time.sleep() function is a call back function from a button so I tried doing something like this:
from tkinter import *
import threading
root = Tk()
schedule_msg_btn = Button(root, text="Schedule Msg", command=lambda : threading.Thread(target=schedule_msg_func).start())
root.mainloop()
It seems to be doing absolutely nothing.

Related

How can I change a label text after a specific time without after() function in tkinter python?

The code i tried to accomplish my result:
from tkinter import*
import time
root=Tk()
lyrics=StringVar()
lyrics.set("unleash")
l=Label(root,textvariable=lyrics)
l.pack()
def change_text():
lyrics.set("you!!")
print('ses')
time.sleep(6.9)
change_text()
mainloop()
The problems in this are like the window is displayed after 10 sec. If I shift the mainloop() before the time.sleep() function the time.sleep() executes after the app is closed. The reason is simple that I didn't use after() function because the after() function doesn't accepts the sec value in decimal.
Any help is appreciated
The after() function takes the delay argument in milliseconds, where you would pass 6900 instead of 6.9. See: https://www.tcl.tk/man/tcl8.4/TclCmd/after.html
The problem is that mainloop() blocks further execution. You can always use a background thread:
def bg_thread():
time.sleep(6.9)
change_text()
thread = threading.Thread(target=bg_thread)
thread.start()
mainloop()
Threading, of course, brings with it a whole host of challenges. It's a good idea to make sure you know what you're doing when working with threads.
See also:
https://docs.python.org/3/library/threading.html#threading.Thread
Tkinter understanding mainloop
When do I need to call mainloop in a Tkinter application?
import time
def update_label():
///update label text here
label.config(text="New label text")
/// wait for 5 seconds before updating the label
time.sleep(5)
update_label()

Tkinter mainloop() not quitting after closing window

This is NOT a duplicate of Python tkinter mainloop not quitting on closing the window
I have an app that builds on tkinter. I observed at sometimes after I close the window using the X button, the code will not execute past the mainloop() line. This happens completely randomly about 10% of chance. Rest of the time, it works like a charm. I would like to ask if there are any way to force it. As I said, the code blocks on the mainloop() line, thus calling sys.exit() after it does not help.
I am using Python 3.9.8.
This is not 100% reproducible, but here is something that might trigger the problem:
from tkinter import *
root = Tk()
Label(root, 'hi').pack()
mainloop()
print('exited')
My first thought is to use root.mainloop() instead of tkinter.mainloop(). This makes a difference if you are using several windows.
That said, I did see this a long time ago on some old OSes. Never figured out the reason, so I just wrote my own quit function, like this:
import tkinter as tk
def _quit():
root.quit()
root.destroy()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", _quit)
tk.Label(root, 'hi').pack()
root.mainloop()
print('exited')

Combining Tkinter mainloop with another event listener

I am trying to build a program that listens for certain key combinations and then shows information to the user in a Tkinter window. To do this, I'm using a keylogger like so (simplified for this example):
from pyHook import HookManager
from pythoncom import PumpMessages
import Tkinter as tk
def on_keyboard_event(event):
label.config(text=event.Key)
root.update()
return True
hm = HookManager()
hm.KeyDown = on_keyboard_event
hm.HookKeyboard()
root = tk.Tk()
label = tk.Label(root, text='Hello world')
label.pack()
PumpMessages()
As expected, the window pops up and shows the user what key they pressed. However, I would like to integrate functionality to show other messages by interacting with the Tkinter window, such as by pressing a button. However, it seems I need Tkinter's mainloop to do this, which I can't figure out how to run alongside PumpMessages(), since it also halts the code similar to mainloop().
I tried running root.mainloop() in a root.after(), and I tried recreating root.mainloop like so:
def mainloop():
root.update()
root.after(50, mainloop)
and then running it right before PumpMessages, but neither of these solutions worked. It also doesn't seem like you can run PumpMessages or root.mainloop in a thread, though I could just not be doing it right. If this is not possible with Tkinter, is there an alternate Python GUI I could use that would make it possible?
You don't need to create a function to use mainloop() so just simply place the mainloop() at the bottom of your code. If you want a delay on it, use root.after(milliseconds, function)
Also, remember to put mainloop() before PumpMessages()
e.g.
def mainloopfunction():
mainloop()
root.after(5000, mainloopfunction)
Hope I could help!

close a window and continue execution in the other one?

I'm really lost...I open a window with two buttons, and when you click on the button called "REGISTER SOME KEY PRESSES" it runs the script called registerSomeKeyPresses.py, BUUUUT once finished I want to close that execution but keep the first window displaying...it's being impossible for me....
Please, i would reaaaally appreciate any help...
Thanks!
#!/usr/bin/env python
from Tkinter import *
import threading
v0 = Tk()
def finishApplication(): v0.destroy()
def registerSomeKeyPresses():
t = threading.Thread(target=execfile("registerSomeKeyPresses.py"))
t.start()
def waitAndRun(f): v0.after(200, f)
b1=Button(v0,text="TERMINAR APLICACION",command=lambda: finishApplication()).pack()
button_keyPresses=Button(v0,text="REGISTER SOME KEY PRESSES",command=lambda: waitAndRun(registerSomeKeyPresses())).pack()
v0.mainloop()
================ registerSomeKeyPresses.py ===========================
Do several things and last command:
io.quit()
When you destroy the instance of Tk, your programm will (and should) exit. If you want to create and destroy windows, create and destroy an instance of Toplevel while keeping the main window active. If you don't want to see the main window you can hide it.
Also, tkinter and threads don't mix very well. You cannot call any methods on any widgets from another thread. I've heard other people say you can call event_generate from another thread, but I think that's the only tkinter function you can call from another thread.
Edit 1
A second try as a response to your comment:
from Tkinter import *
from subprocess import call
import sys
t = Tk()
def click():
t.iconify()
try:
call([sys.executable, 'script.py'])
finally:
t.deiconify() # if it should close do t.quit() and t.destroy()
b = Button(t, command= click)
b.pack()
t.mainloop()
Old Version
What does that do?
================ registerSomeKeyPresses.py ===========================
v0.quit()
v0.destroy()
io.mainloop()
An other error is:
threading.Thread(target=execfile, args = ("registerSomeKeyPresses.py",))
if you really neeed a thread.
Do never mix tkinter mainloop things with threads. Threads can use event_generate - thats safe.

Automatically close window after a certain time

In a class, in a function I am creating a Tkinter Canvas. This function is being called by another class, I would like for the Tkinter window to pop up for 30 seconds and then close itself. I have it call
master.mainloop()
time.sleep(30)
master.destroy()
But I get an error
"elf.tk.call('destroy', self._w)
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed"
So how can I have it close itself?
Don't use time.sleep() with tkinter. Instead, call the function after on the widget you want to close.
Here it is the most simple example:
import tkinter as tk
w = tk.Tk()
w.after(30000, lambda: w.destroy()) # Destroy the widget after 30 seconds
w.mainloop()
The problem here is that mainloop() does not return until the GUI has shut down.
So, 30 seconds after the GUI has shut down and destroyed itself, you try to destroy it. And obviously that fails.
But you can't just move the sleep and destroy calls inside the main loop, because if you sleep in the middle of the main loop, the GUI will freeze up.
So, you need some kind of timer that will not stop the main loop. tkinter includes the after method for exactly that purpose. This answer gives a detailed example of using it.

Categories