I'm trying to create a mouse loop application, I can get the start button to work inside the loop, however the end button seems to crash tkinter instead of close the application.
any advise would be appreciated
import pyautogui
from tkinter import *
import sys
window = Tk()
def start_loop():
while True:
pyautogui.moveTo(3131, 891, 1)
pyautogui.PAUSE = 180
pyautogui.moveTo(3128, 434, 1)
b1 = Button(window, text="Start Mouse", command=start_loop)
b1.grid(row=0, column=0)
def end_loop():
print(sys.exit())
b2 = Button(window, text="Stop Mouse", command=end_loop)
b2.grid(row=0, column=1)
window.mainloop()
This can also be achieved using threading.
It will consume more resources, but I think it would be less "clunky" as a end result.
I added comments about the timing and some print statements to track what is happening:
import pyautogui
from tkinter import *
import threading
import sys
import time
window = Tk()
t = False
def start_thread():
"""Starts the Mouse thread"""
global t
if t:
return
t = threading.Thread(target = start_loop)
setattr(t, "flag", False)
t.start()
print("Mouse thread Started")
def start_loop():
"""Loop that moves the mouse"""
t = threading.current_thread()
time.sleep(0.1) # Just for better printing
while True:
print("Mouse thread Running")
pyautogui.moveTo(3131, 891, 1)
# Not sure you wanted to adjust the pyautogui time to 180, or just wait 180 seconds.
#pyautogui.PAUSE = 180
for x in range(180):
time.sleep(1)
flag = getattr(t, "flag")
if flag:
print("Mouse thread Stopped")
return
pyautogui.moveTo(3128, 434, 1)
def end_loop():
"""Ends the mouse loop"""
global t
if not t:
return
setattr(t, "flag", True)
while t.is_alive():
time.sleep(0.1)
t = False
b1 = Button(window, text="Start Mouse", command=start_thread)
b1.grid(row=0, column=0)
b2 = Button(window, text="Stop Mouse", command=end_loop)
b2.grid(row=0, column=1)
window.mainloop()
You should use after function.
In tkinter, it should check each component of interfaces with loop (I mean mainloop).
However, if you use while True:, then the GUI cannot check the reactions about the other functions or interfaces like Button.
So, your program can be downed.
import pyautogui
from tkinter import *
import sys
window = Tk()
def start_loop():
pyautogui.moveTo(3131, 891, 1)
pyautogui.PAUSE = 1.8
pyautogui.moveTo(3128, 434, 1)
window.after(180, start_loop) //after(ms_for_delay, function)
b1 = Button(window, text="Start Mouse", command=start_loop)
b1.grid(row=0, column=0)
def end_loop():
sys.exit()
b2 = Button(window, text="Stop Mouse", command=end_loop)
b2.grid(row=0, column=1)
window.mainloop()
Try this code.
Related
In the code below, when I press Button 1, the application does not respond and I cannot use other Button 2.
Threading module was required for this.
I have created Threads for Threadingle main() and process(), but when the program opens and I press Button 1, the application does not respond and I cannot press Button 2.
What's the problem?
from tkinter import *
import threading
def process():
while True:
print("Hello World")
processThread = threading.Thread(target=process)
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton=Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red",fg="white", width="15", command=processThread.run)
recButton.place(x=15,y=10)
stopButton=Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange",fg="white", width="15", command="")
stopButton.place(x=15,y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
use processThread.start to start the thread instead of processThread.run infact processThread.run will just call the process method and which will not return control to your event loop hence application goes wild
command=processThread.start
from tkinter import *
import threading
def process():
while True:
print("Hello World")
processThread = threading.Thread(target=process)
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton=Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red",fg="white", width="15", command=processThread.start)
recButton.place(x=15,y=10)
stopButton=Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange",fg="white", width="15", command="")
stopButton.place(x=15,y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
Modified the above code for start and stop button functionality. Here we are using flag when this is true thread will be running if flag if false the thread will stop. Remember we can't start a stopped thread because of this we have to create new thread.
import time
from tkinter import *
import threading
flag = True
def printHello():
global flag
while flag:
print("Hello World")
time.sleep(1)
"""
starts the thread
"""
def startProcess():
global flag
flag = True
processThread = threading.Thread(target=printHello)
processThread.start()
"""
stops the thread
"""
def stopProcess():
global flag
flag = False
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton = Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red", fg="white", width="15",
command=startProcess)
recButton.place(x=15, y=10)
stopButton = Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange", fg="white", width="15", command=stopProcess)
stopButton.place(x=15, y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
So I am making a stopwatch program with tkinter.
At the moment, I am in the process of implementing a stop button for the stopwatch.
I can start the stopwatch and it will update the Label fine but my end button isn't working and I can't seem to figure out why.
Here's my code so far:
from tkinter import *
from time import sleep
import threading
root = Tk()
root.title("Clock")
class Buttons:
def __init__(self, master):
self.menu = Menu(master)
master.config(menu=self.menu)
self.menu.add_command(label="Stopwatch")
self.menu.add_command(label="Timer")
self.menu.add_command(label="Quit", command=quit)
self.stopwatchStart = Button(master, text="Start", command=self.test_start)
self.stopwatchStart.config(height=2, width=5)
self.stopwatchStart.grid(row=0)
self.stopwatchEnd = Button(master, text="End", command=self.stopwatch_End)
self.stopwatchEnd.config(height=2, width=5)
self.stopwatchEnd.grid(row=1)
self.labelSeconds = Label(master, text="Seconds:")
self.labelSeconds.grid(row=0, column=1)
self.stopwatchSeconds = Label(master, text="0")
self.stopwatchSeconds.grid(row=0, column=2)
def test_start(self):
print("Starting thread")
t = threading.Thread(target=self.stopwatch_Start)
t.setDaemon(True)
t.start()
def stopwatch_Start(self, test=False):
print("Stopwatch started")
stopwatch_seconds = "0"
stopwatchBreak = test
print(f"stopwatchBreak == {stopwatchBreak}")
while not stopwatchBreak:
print(stopwatch_seconds)
stopwatch_seconds = int(stopwatch_seconds)
stopwatch_seconds += 1
self.stopwatchSeconds.config(text=stopwatch_seconds)
root.update()
sleep(1)
print("Stopping stopwatch")
return
def stopwatch_End(self):
Buttons.stopwatch_Start(self, True)
print("Attempting to end")
b = Buttons(root)
root.mainloop()
I am using threading to run the stop watch and the window from tkinter at the same time by the way.
Also I have put several print() across the functions to see what is succeeding and what isn't. I think the problem may have something to do with the thread in test_start(). The loop that won't end when I click the end button is in stopwatch_Start(). It just keeps counting up.
I have received no error messages
Does anyone have any suggestions/alternatives to stopping the stopwatch?
look at this code
def stopwatch_End(self):
Buttons.stopwatch_Start(self, True)
print("Attempting to end")
this is your problem =>
Buttons.stopwatch_Start(self, True)
correct this with:
self.stopwatch_Start(True)
here you are call "stopwatch" on the class Buttons, this is an unbound function!!!
"Buttons.stopwatch" is different than "self.stopwatch" the later is bound to the instance.
from tkinter import *
from time import sleep
from threading import Thread
from threading import Event
root = Tk()
root.title("Clock")
class Buttons:
def __init__(self, master):
self.evt = Event()
self.menu = Menu(master)
master.config(menu=self.menu)
self.menu.add_command(label="Stopwatch")
self.menu.add_command(label="Timer")
self.menu.add_command(label="Quit", command=quit)
self.stopwatchStart = Button(master, text="Start", command=self.test_start)
self.stopwatchStart.config(height=2, width=5)
self.stopwatchStart.grid(row=0)
self.stopwatchEnd = Button(master, text="End", command=self.stopwatch_End)
self.stopwatchEnd.config(height=2, width=5)
self.stopwatchEnd.grid(row=1)
self.labelSeconds = Label(master, text="Seconds:")
self.labelSeconds.grid(row=0, column=1)
self.stopwatchSeconds = Label(master, text="0")
self.stopwatchSeconds.grid(row=0, column=2)
def test_start(self):
print("Starting thread")
t = Thread(target=self.stopwatch_Start)
t.setDaemon(True)
t.start()
def stopwatch_Start(self, test=False):
print("Stopwatch started")
stopwatch_seconds = "0"
stopwatchBreak = test
print(f"stopwatchBreak == {stopwatchBreak}")
# self.evt.is_set() will force the loop to check its actually state if it True or not
while not self.evt.is_set():
print(stopwatch_seconds)
stopwatch_seconds = int(stopwatch_seconds)
stopwatch_seconds += 1
self.stopwatchSeconds.config(text=stopwatch_seconds)
root.update()
sleep(1)
print("Stopping stopwatch")
return
def stopwatch_End(self):
#we set self.evt to True so that the while loop will be broken
self.evt.set()
print("Attempting to end")
b = Buttons(root)
root.mainloop()
I made the following program to change screen brightness depending on the time of day. It uses an infinite loop to constantly check the time, but this also prevents the user from changing the values in the tkinter window or from closing it. Is there any way to avoid this?
from datetime import datetime
import screen_brightness_control as sbc
from tkinter import *
m=Tk()
m.title('Brightness Control')
def saver():
print("Saving")
global brit
brit=e3.get()
global frot
frot=e1.get()
global tot
tot=e2.get()
a=True
while a==True:
current_brightness=sbc.get_brightness()
now=datetime.now().time()
if now.hour>int(frot) and now.hour<int(tot) :
sbc.set_brightness(brit)
else:
sbc.set_brightness(40)
Label(m, text='Brightness').grid(row=0,column=0)
Label(m, text='From').grid(row=1,column=0)
Label(m, text='To').grid(row=1,column=2)
e1 = Entry(m)
e2 = Entry(m)
e3 = Entry(m)
e1.grid(row=1, column=1)
e2.grid(row=1, column=3)
e3.grid(row=0, column=1)
button = Button(m, text='Save', width=5,command=saver)
button.grid(row=2,column=3)
m.mainloop()
You have to add <tk.Tk object>.update() anywhere in your while loop like this:
import tkinter as tk
root = tk.Tk()
button = tk.Button(root, text="This is a button")
button.pack()
while True:
root.update()
root.update() updates tcl and that stops the "program isn't responding" message.
I have a problem with stopping the program. When I click exit button, the mainloop stops but program is still running. I am new to it and I have no idea what to do. I know the issue is the thread is still running and I don't know how to stop it.
This is my code:
from tkinter import *
import simulation as s
import graph as g
import numpy as np
from tkinter import filedialog
import threading
def main():
root = Tk()
root.title("Simulator")
switch = True
def get_filename():
return filedialog.askopenfilename(parent=root)
def play():
def run():
while switch:
s.simulation(s.particle, np.inf, s.initial_time_step, get_filename())
thread = threading.Thread(target=run)
thread.start()
def start_simulation():
global switch
switch = True
v.set('Simulation is running!')
play()
def stop_simulation():
global switch
v.set('Simulation is stopped!')
switch = False
def draw_graphs():
g.create_graphs()
start = Button(root, text='Start simulation', command=start_simulation, width=50)
start.pack()
finish = Button(root, text='Stop simulation', command=stop_simulation, width=50)
finish.pack()
graphs = Button(root, text='Graphs', command=draw_graphs, width=50)
graphs.pack()
exit_button = Button(root, text='Exit', command=root.destroy, width=50)
exit_button.pack()
v = StringVar()
statement = Label(root, textvariable=v)
statement.pack()
root.mainloop()
if __name__ == '__main__':
main()
Create a function for the exit button which also stops your thread
def exit():
switch = False
root.destroy()
And later:
exit_button = Button(root, text='Exit', command=exit, width=50)
You can also call the same method whenever the window is closed with the top right X button. Just bind your method to the event:
root.protocol("WM_DELETE_WINDOW", exit)
Ps: You dont need to use global here, because your nested functions have access to the outer functions variables
I have this program which beeps every second until it's stopped. The problem is that after I press "Start" and the beeps starts, I cannot click the "Stop" button because the window freezes. Any help is welcome.
#!/usr/bin/python
import Tkinter, tkMessageBox, time, winsound, msvcrt
running = True
Freq = 2500
Dur = 150
top = Tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
sec = 0
while running:
if sec % 1 == 0:
winsound.Beep(Freq, Dur)
time.sleep(1)
sec += 1
def stop():
running = False
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
There are several things wrong with your code. First of all you shouldn't use time.sleep() in a Tkinter program because it interferes with the mainloop(). Instead one typically uses the universal widget method .after() to schedule a function to run after a specified delay.
Secondly you're not using global variables correctly. When you assign a value to a named variable in a function, it will create a local variable unless that name has been previous declared global. So for instance, your stop() function is creating a local variable named running and setting its value to 0, not changing the value of the global variable with the same name.
The previous rule doesn't apply to just referencing (reading) the current value of a variable. That is why it was OK to not have declared Freq and Dur globals in start().
Another problem is with the sec % 1 == 0 in your start() function. Any value % 1 is 0. To check odd/evenness use sec % 2.
Here's a working version which has also been reformatted to follow PEP 8 - Style Guide for Python Code more closely.
try:
import tkinter as tk
except ModuleNotFoundError:
import Tkinter as tk # Python 2.
import winsound
FREQ = 2500
DUR = 150
after_id = None
secs = 0
def beeper():
global after_id
global secs
secs += 1
if secs % 2 == 0: # Every other second.
winsound.Beep(FREQ, DUR)
after_id = top.after(1000, beeper) # Check again in 1 second.
def start():
global secs
secs = 0
beeper() # Start repeated checking.
def stop():
global after_id
if after_id:
top.after_cancel(after_id)
after_id = None
if __name__ == '__main__':
top = tk.Tk()
top.title('MapAwareness')
top.geometry('200x100')
startButton = tk.Button(top, height=2, width=20, text="Start", command=start)
stopButton = tk.Button(top, height=2, width=20, text="Stop", command=stop)
startButton.pack()
stopButton.pack()
top.mainloop()
Update
Since this answer has become fairly popular, I'd like touch on another slightly more advanced topic — namely how making the code more object-oriented would simplify things by eliminating the need almost all of the global variables.
try:
import tkinter as tk
except ModuleNotFoundError:
import Tkinter as tk # Python 2.
import winsound
FREQ = 2500
DUR = 150
class Application(tk.Frame, object):
def __init__(self, master=None):
super(Application, self).__init__(master) # Call base class initializer.
self.after_id = None
self.secs = 0
# Create widgets,
startButton = tk.Button(top, height=2, width=20, text="Start", command=self.start)
stopButton = tk.Button(top, height=2, width=20, text="Stop", command=self.stop)
startButton.pack()
stopButton.pack()
def beeper(self):
self.secs += 1
if self.secs % 2 == 0: # Every other second.
winsound.Beep(FREQ, DUR)
self.after_id = top.after(1000, self.beeper) # Check again in 1 second.
def start(self):
self.secs = 0
self.beeper() # Start repeated checking.
def stop(self):
if self.after_id:
top.after_cancel(self.after_id)
self.after_id = None
if __name__ == '__main__':
top = tk.Tk()
app = Application()
app.master.title('MapAwareness')
app.master.geometry('200x100')
app.mainloop()
You code have top.mainloop() which has a while loop running inside it and on top of that you also have a while loop inside def start():. So it is like loop inside loop.
You can create a function that does what you want for the body of the loop. It should do exactly one iteration of the loop. Once it is done, it needs to arrange for itself to be called again some time in the future using after. How far in the future defines how fast your loop runs.
And you can then use after_cancel to cancel the event. Below code worked for me
import Tkinter, tkMessageBox, time, winsound, msvcrt
Freq = 2500
Dur = 150
top = tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
global job1
if running == True:
winsound.Beep(Freq, Dur)
job1 = top.after(1000, start) # reschedule event in 1 seconds
def stop():
global job1
top.after_cancel(job1)
startButton = tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
#top.after(1000, start)
top.mainloop()
The problem is that the while loop in start() blocks the GUI handler mainloop(). Try using Tk.after() in start():
def start(force=True):
global running
if force:
running = True
if running:
winsound.Beep(Freq, Dur)
top.after(1000, start, False)
And change stop():
def stop():
global running
running = False
Beaten to the punch again but here goes nothing. As above use the after function to prevent the mainloop blocking.
See:
tkinter: how to use after method
#!/usr/bin/python
import Tkinter, tkMessageBox, time
Freq = 2500
Dur = 150
top = Tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
print ("Beep")
top.after(1000, start)
def stop():
print ("Stop")
top.quit()
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
I used thread and global variable to fit your need. Not so complicated if you understand how they work. Just an addition of few lines and minor change to your existing line, and it works. Look through to see the changes made to your original code.
#!/usr/bin/python
import tkinter
from tkinter import messagebox
import time, winsound, msvcrt
from threading import Thread
running = True
Freq = 2500
Dur = 150
top = tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def button_click():
global running #create global
running = True
# Create new thread
t = Thread(target = start)
# Start new thread
t.start()
def start():
sec = 0
while running:
if running == False:
break
if sec % 1 == 0:
winsound.Beep(Freq, Dur)
time.sleep(1)
sec += 1
def stop():
global running #create global
running = False
startButton = tkinter.Button(top, height=2, width=20, text ="Start", command = button_click) #Change to call button_click instead start
stopButton = tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
from threading import Thread
def stttart():
t1=Thread(target=start)
t1.start()
def start():
...
def stop():
...
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = stttart)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
https://www.geeksforgeeks.org/how-to-use-thread-in-tkinter-python/
last year, this had been my big problem for some months