tkinter - how to update button text before command function ends? - python

I have read numerous questions here but none actually answered my puzzle:
openFolderBtnGenerate = Button(top, text="Generate", command=lambda: compute(csv_file_name, lan, lon, alt))
openFolderBtnGenerate.grid(row=5, column=1)
def compute(csv_file_name, lat_0, lon_0, alt_0):
global openFolderBtnGenerate
openFolderBtnGenerate['text'] = "Please wait..."
# here mathematical computation code that takes 10 seconds to accomplish
...
# end
I intended the button text to change before the computation starts so users understand it might take some time to accomplish.
What really happens is that the button text is changed only after the computation ends.
How can I make the button text change right after the button is clicked, without having to wait these long 10 seconds?

What I think you want to do is just to change text as you press the button then run the function, so a small thing you can do is make another function CODE :
Def function2()
global openFolderBtnGenerate
openFolderBtnGenerate['text'] = "Please wait..."
compute(csv_file_name, lan, lon, alt)
openFolderBtnGenerate = Button(top, text="Generate", command=function2)
openFolderBtnGenerate.grid(row=5, column=1)
def compute(csv_file_name, lat_0, lon_0, alt_0):
# here mathematical computation code that takes 10 seconds to accomplish
...
# end
It works for me and will surely work for you too.

See line "root.update_idletasks()":
import time
import tkinter as tk
root = tk.Tk()
def start_task():
btn_text.set("Please wait for task to finish...")
btn.update_idletasks()
time.sleep(2)
btn_text.set("Press to start a task")
btn_text = tk.StringVar()
btn = tk.Button(root, textvariable=btn_text, command=start_task)
btn_text.set("Press to start a task")
btn.pack()
root.mainloop()
And another sample, without time.sleep() (for UI not to freeze):
import time
import tkinter as tk
root = tk.Tk()
def update_text():
btn_text.set("Press to start a task")
def start_task():
btn_text.set("Please wait for task to finish...")
btn.update_idletasks()
root.after(2000, update_text)
btn_text = tk.StringVar()
btn = tk.Button(root, textvariable=btn_text, command=start_task)
btn_text.set("Press to start a task")
btn.pack()
root.mainloop()

Related

Button Disable on press tkinter

I'm making a mining game and whenever a user clicks 'mine' button, I want it to disable so the user cant click it again until cool down wears off. I made a sample of the code, I state the definition first, then make the button, but since the button is after, the def doesn't know what variable the 'mine' button is. Any help appreciated!
root = Tk()
def def1():
btn[state] = 'disabled'
Btn = Button(root, text="button", command= def1())
root.mainloop()```
Try this:
import tkinter as tk
def enable_btn():
btn.config(state="normal")
def def1():
print("Clicked")
btn.config(state="disabled")
# 1000 is the cooldown in ms (so 1000 = 1 sec)
btn.after(1000, enable_btn)
root = tk.Tk()
btn = tk.Button(root, text="button", command=def1)
btn.pack()
root.mainloop()
I am using a .after script so the enable_btn function runs 1 sec after def1 is called.

Tkinter get status message from called function displayed in real time

I try to show a status from a called function in real time. However, the messages appears in the GUI all at ones after function is done. What can I do?
Thanks for your help!
from tkinter import *
import time
def sleep():
msgbox.insert(INSERT,"go sleep...\n")
time.sleep(2)
msgbox.insert(INSERT,"... need another 2 sec \n")
time.sleep(2)
msgbox.insert(INSERT,"... that was good\n")
return
root = Tk()
root.minsize(600,400)
button= Button(text="Get sleep", command=sleep)
button.place(x=250, y=100,height=50, width=100)
msgbox =Text(root, height=10, width=60)
msgbox.place(x=20, y=200)
mainloop()
You can use the Tk.update() function to update the window without having to wait for the function to finish:
from tkinter import *
import time
def sleep():
msgbox.insert(INSERT,"go sleep...\n")
root.update()
time.sleep(2)
msgbox.insert(INSERT,"... need another 2 sec \n")
root.update()
time.sleep(2)
msgbox.insert(INSERT,"... that was good\n")
root = Tk()
root.minsize(600,400)
button= Button(text="Get sleep", command=sleep)
button.place(x=250, y=100,height=50, width=100)
msgbox =Text(root, height=10, width=60)
msgbox.place(x=20, y=200)
mainloop()
(also the return is unnecessary, you use it if you want to end a function partway through or return a value from the function).
Let me know if you have any problems :).

How to add time (ticks) in a GUI for a game

I want to add the ticking of time in seconds to a GUI, for a clicker game. So the idea is to have a function that gets called every n ticks, and this function increments X objects.
I have tried to use a while loop, both before and after calling the .mainloop() method. It didn't work in either occasion, I also tried the crazy idea of having the mainloop() method inside the while loop (aware of what that would do lol).
from tkinter import *
import time
result = 0
window = Tk()
window.title("Numbers Game")
window.geometry('360x240')
label = Label(window, text=result)
label.grid(column=0,row=0)
def clicked():
global result
result += 1
label.config(text=result)
button = Button(window, text="Push Me", command=clicked)
button.grid(column=1, row=2)
window.mainloop()
while True:
time.sleep(1)
clicked()
The current version of my code produces an error that mentions the function doing GUI related things outside of the window. But I don't have the slightest clue of how to achieve this.
You mean you want to have the result counter increment every second? You can't use infinite loops with a GUI, because they interfere with the GUI's mainloop. You have to integrate your code into the mainloop using the after method.
from tkinter import *
import time
result = 0
window = Tk()
window.title("Numbers Game")
window.geometry('360x240')
label = Label(window, text=result)
label.grid(column=0,row=0)
def clicked():
global result
result += 1
label.config(text=result)
def tick():
clicked()
window.after(1000, tick) # after 1,000 milliseconds, call tick() again
button = Button(window, text="Push Me", command=clicked)
button.grid(column=1, row=2)
tick() # start the "loop"
window.mainloop()

Function that counts down as a subprocess

I have a problem in which I am running a Tkinter GUI program (a quiz game). While the user has a choice of 4 buttons and can choose one, I need a countdown timer that will change the question when the time is zero. I need it as a subprocess or separate thread because the user will not be able to choose an answer otherwise.
This is different from other questions about timers because the answers to those questions include [Object] = threading.Timer(numCount, callback), but the Timer does not return its value as it counts.
Is there any way to do this? I have already tried multiple methods, including the threading module, and the pygame clock (:D).
Multithreading might not be necessary: You can use the after method to change the question when time has passed, while keeping your GUI reactive:
In the following example, the question changes every 10 seconds.
import tkinter as tk
def countdown(t):
cdn['text'] = f'{t}'
if t > 0:
root.after(1000, countdown, t-1)
def change_question(idx):
lbl['text'] = questions[idx % 2]
root.after(10000, change_question, idx+1)
countdown(10)
def clickme(t):
print(f"{lbl['text']} : {t}")
if __name__ == '__main__':
questions = ['Is multi-threading necessary?', 'Is simple better than complicated?']
root = tk.Tk()
bt1 = tk.Button(root, text='Yes', command=lambda: clickme('Yes'))
bt2 = tk.Button(root, text='No', command=lambda: clickme('No'))
bt3 = tk.Button(root, text='Maybe', command=lambda: clickme('Maybe'))
bt4 = tk.Button(root, text='No Idea', command=lambda: clickme('No Idea'))
lbl = tk.Label(root, text='')
cdn = tk.Label(root, text='')
cdn.pack()
lbl.pack()
bt1.pack()
bt2.pack()
bt3.pack()
bt4.pack()
change_question(0)
root.mainloop()
sample output:
Is multi-threading necessary? : No
Is simple better than complicated? : Yes
You can use signal:
def myfunc(sig, frame):
print("timer fired")
signal.signal(signal.SIGALRM, myfunc)
signal.alarm(4) # seconds

tkinter Button click to start thread to prevent GUI from freezing

I have a tkinter interface with two buttons to trigger two modes of my application and a label which shows which mode it is in. When a button is clicked, the resulting function that is called takes a while to execute and come back. This results in my mouse cursor spinning and the button being essentially "frozen" until the function finishes its execution before I can click another button.
The behavior that I want is that I click a button, it runs the function asynchronously (new thread) and leaves the buttons clickable again. If another button is clicked, that first thread immediately is killed and the new function/thread starts up asynchronously.
How do I go about achieving this?
def alert_mode(var):
print("Entering Alert Mode")
var.set("Current mode: Alert")
// do stuff that takes a while to return
def capture_mode(var):
print("Entering Capture Mode")
var.set("Current mode: Capture")
// do stuff that takes a while to return
root = tk.Tk()
root.geometry('400x400')
var = StringVar()
var.set("Current mode: Alert")
text = tk.Label(root, text="Current mode: Alert", textvariable=var, fg="blue", font=("Arial", 18))
text.pack(pady=8)
b = tk.Button(root, text="Alert mode", height=10, width=15, font=("Arial",14), command=lambda: alert_mode(var))
b.pack()
b2 = tk.Button(root, text="Capture mode", height=10, width=15, font=("Arial",14), command=lambda: capture_mode(var))
b2.pack()
root.mainloop()
Edit:
Okay, here is the updated code. I tried to figure out how to start and stop a thread but this doesn't quite work the way I want it to. I actually don't want both threads to be running at once. It's either alert mode or capture mode. The point is that I don't want the GUI to freeze up while either mode is running so that the user can switch to the other mode at any time.
def alert_mode(var):
print("Entering Alert Mode")
var.set("Current mode: Alert")
// do stuff that takes a while to return
def capture_mode(var):
print("Entering Capture Mode")
var.set("Current mode: Capture")
// do stuff that takes a while to return
def start_alert_thread(var):
t = threading.Thread(target=alert_mode, args=(var,))
t.start()
t.join()
def start_capture_thread(var):
t2 = threading.Thread(target=alert_mode, args=(var,))
t2.start()
t2.join()
root = tk.Tk()
var = StringVar()
var.set("Current mode: Alert")
text = tk.Label(root, text="Current mode: Alert", textvariable=var, fg="blue", font=("Arial", 18))
text.pack(pady=8)
b = tk.Button(root, text="Alert mode", height=10, width=15, font=("Arial",14), command=lambda: start_alert_thread(var))
b.pack()
b2 = tk.Button(root, text="Capture mode", height=10, width=15, font=("Arial",14), command=lambda: start_capture_thread(var))
b2.pack()
root.mainloop()
Use threading,
import threading
option = 0
def buttonOne():
global option
if option == 2:
"kill statement (not sure what)"
option = 1
else:
option = 1
def buttonTwo():
global option
if option == 1:
"kill statement (not sure what)"
option = 2
else:
option = 2
have the button run a function like this.
def threadButtonOne():
threading.Thread(target=buttonOne).start()
I got way out for such condition:
Condition:
Running selenium webdriver with python and tkinter. When play button is pressed from where handle goes to python program, tkinter GUI window turns into "Not responding" mode, but python program continues.
Note: indent- is just a placeholder, like indentation is important in python
Solution:
import threading, tkinter # and so on
def python_main( ):
input("Enter number of users") #and so on.
def python_sub( ): #and complete program of multiple functions
pass
tkwindow = tk.Tk()
def mid():
th = threading.Thread(target=python_main)
th.start()
def tkinter_func():
button = tk.Button(tkwindow, text= "press me", border=0, command=mid)
button.place(x=4,y=4)
tkwindow.mainloop()
Description: No need for wait. NO auto run program.

Categories

Resources