How to break out of while loop with button release - python

I have a button that when pressed is supposed to keep printing a certain phrase. However I have an if statement that's supposed to break out of the while loop if it reads that the button is released. Essentially, I'm trying to create a gui that, when a specific button is pressed it continues to act out a function until that button is released in tkinter. I believe that there should be a statement which reads the state of the button and knows when the button is released, but I don't know what it is.
self.button.pack(side="top")
self.vsb.pack(side="right", fill="y")
self.text.pack(side="bottom", fill="x")
self.button.bind("<ButtonPress>", self.on_press)
self.button.bind("<ButtonRelease>", self.on_release)
def on_press(self, event):
while True:
time.sleep(1)
self.log("button was pressed")
if (what do I put here):
break

you can't enter a dead loop while handling an even. try using threads instead.
here is a VERY simple example:
def on_press(self, event):
self._running = True
threading.Thread(self.my_loop).start()
def my_loop(self):
while self.running:
time.sleep(1)
print("loop is done")
def on_release(self, event):
self._running = False # this will stop the loop in the other thread...

Related

Python auto clicker, how to listen for mouse events

I can't figure out how to make auto clicks start when I press the left mouse button and stop when I release it. Maybe someone knows how to solve it?
Perhaps with the help of pynput it is not advisable to do this, but it is better to use pyautogui, or there are some other solutions.
# importing time and threading
import time
import threading
from pynput.mouse import Button, Controller
# pynput.keyboard is used to watch events of
# keyboard for start and stop of auto-clicker
from pynput.keyboard import Listener, KeyCode
# four variables are created to
# control the auto-clicker
delay = 0.277
button = Button.left
start_stop_key = KeyCode(char='+') #The left mouse button should be here
stop_key = KeyCode(char='-')
# threading.Thread is used
# to control clicks
class ClickMouse(threading.Thread):
# delay and button is passed in class
# to check execution of auto-clicker
def __init__(self, delay, button):
super(ClickMouse, self).__init__()
self.delay = delay
self.button = button
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
# method to check and run loop until
# it is true another loop will check
# if it is set to true or not,
# for mouse click it set to button
# and delay.
def run(self):
while self.program_running:
while self.running:
mouse.click(self.button)
time.sleep(self.delay)
time.sleep(0.1)
# instance of mouse controller is created
mouse = Controller()
click_thread = ClickMouse(delay, button)
click_thread.start()
# on_press method takes
# key as argument
def on_press(key):
# start_stop_key will stop clicking
# if running flag is set to true
if key == start_stop_key:
if click_thread.running:
click_thread.stop_clicking()
print("click end")
else:
click_thread.start_clicking()
print("click start")
# here exit method is called and when
# key is pressed it terminates auto clicker
elif key == stop_key:
click_thread.exit()
listener.stop()
with Listener(on_press=on_press) as listener:
listener.join()
I searched for a solution but didn't understand anything.
I found a solution.
It turned out that using the left mouse button when it is pressed is not the best solution, but rather not working.
You need to use an unoccupied key, in my case it is the middle mouse key. I replaced the function on_press with the function on_click, which was presented in the pynput documentation, but thereby lost the functionality of completing the script on the key.
Here is the code:
# importing time and threading
import time
import threading
from pynput.mouse import Button, Controller
from pynput import mouse
delay = 0.277
button = Button.left
class ClickMouse(threading.Thread):
def __init__(self, delay, button):
super(ClickMouse, self).__init__()
self.delay = delay
self.button = button
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
def run(self):
while self.program_running:
while self.running:
muse.click(self.button)
time.sleep(self.delay)
time.sleep(0.1)
# instance of mouse controller is created
muse = Controller()
click_thread = ClickMouse(delay, button)
click_thread.start()
def on_click(x, y, button, pressed):
if button == button.middle:
if pressed:
click_thread.start_clicking()
print("click start")
elif not pressed:
click_thread.stop_clicking()
print("click end")
with mouse.Listener(on_click=on_click) as listener:
listener.join()

Modifying Auto-Clicker Python Code to Use Mouse Button to Start and Stop

I'm using the code from this website, https://www.geeksforgeeks.org/how-to-make-a-python-auto-clicker/, and I feel like I have it setup correctly but I get some strange errors when I trigger the auto clicker. I'm modifying it to use a button on my mouse as the start and stop button instead of keys on my keyboard. I actually don't even need pynput.keyboard. My script runs but I get these errors when I press the button on my mouse:
Here's my code:
# importing time and threading
import time
import threading
# from pynput import mouse
from pynput.mouse import Listener, Button, Controller
# pynput.keyboard is used to watch events of
# keyboard for start and stop of auto-clicker
# from pynput.keyboard import KeyCode
# four variables are created to
# control the auto-clicker
delay = 0.001
button = Button.left
start_stop_key = Button.x2
stop_key = Button.x2
# threading.Thread is used
# to control clicks
class ClickMouse(threading.Thread):
# delay and button is passed in class
# to check execution of auto-clicker
def __init__(self, delay, button):
super(ClickMouse, self).__init__()
self.delay = delay
self.button = button
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
# method to check and run loop until
# it is true another loop will check
# if it is set to true or not,
# for mouse click it set to button
# and delay.
def run(self):
while self.program_running:
while self.running:
mouse.click(self.button)
time.sleep(self.delay)
time.sleep(0.1)
# instance of mouse controller is created
mouse = Controller()
click_thread = ClickMouse(delay, button)
click_thread.start()
# on_click method takes
# key as argument
def on_click(key):
# start_stop_key will stop clicking
# if running flag is set to true
if key == start_stop_key:
if click_thread.running:
click_thread.stop_clicking()
else:
click_thread.start_clicking()
# here exit method is called and when
# key is pressed it terminates auto clicker
elif key == stop_key:
click_thread.exit()
listener.stop()
with Listener(on_click=on_click) as listener:
listener.join()
I'm just not sure what I am missing at this point. Any help would be greatly appreciated.

How to make a hold to click autoclicker?

This autoclicker script I found automatically clicks the mouse when you press the "s" key. However, I want to change it so that the autoclicker will only run when the left mouse button is depressed. I have spent a surplus of 10 hours trying to figure this one out, but frankly, I'm terrible at coding. Any help would be great. (Also, there is no GetKeyState or GetASyncKeyState in any library I know of)
My code is:
import threading
from pynput.mouse import Button, Controller
from pynput.keyboard import Listener, KeyCode
delay = 0.01
button = Button.left
start_stop_key = KeyCode(char='s')
exit_key = KeyCode(char='e')
class ClickMouse(threading.Thread):
def __init__(self, delay, button):
super(ClickMouse, self).__init__()
self.delay = delay
self.button = button
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
def run(self):
while self.program_running:
while self.running:
mouse.click(self.button)
time.sleep(self.delay)
mouse = Controller()
click_thread = ClickMouse(delay, button)
click_thread.start()
def on_press(key):
if key == start_stop_key:
if click_thread.running:
click_thread.stop_clicking()
else:
click_thread.start_clicking()
elif key == exit_key:
click_thread.exit()
listener.stop()
with Listener(on_press=on_press) as listener:
listener.join()
you have to go to microsoft to get the Virtual-Key codes for input.
For left mouse buttons, the code is 0x01. for Right mouse buttons the code is 0x02.
You will also need to install pywin32 and import the win32 api
Then you can do something like this:
```while True:
a = win32api.GetKeyState(0x01)
print(a)
time.sleep(255.0) #this should be long enough
Basically, when this is run where there's no input pressed, the script will print 0 indefinitely. However when it is pressed, it will print a 1 for as long as it's pressed.
From there you can make that into an if/then statement and activate the clicking function.

why does this run() command run without anything calling it?

so i was messing around with python when i came across this autoclicker code on the internet
import time
import threading
from pynput.mouse import Button, Controller
from pynput.keyboard import Listener, KeyCode
delay = 0.001
button = Button.left
start_stop_key = KeyCode(char='s')
exit_key = KeyCode(char='e')
class ClickMouse(threading.Thread):
def __init__(self, delay, button):
super(ClickMouse, self).__init__()
self.delay = delay
self.button = button
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
def run(self):
while self.program_running:
while self.running:
mouse.click(self.button)
time.sleep(self.delay)
time.sleep(0.1)
mouse = Controller()
click_thread = ClickMouse(delay, button)
click_thread.start()
def on_press(key):
if key == start_stop_key:
if click_thread.running:
click_thread.stop_clicking()
else:
click_thread.start_clicking()
elif key == exit_key:
click_thread.exit()
listener.stop()
with Listener(on_press=on_press) as listener:
listener.join()
after a bit of research i understand most of it but im still confused about one thing,
why does the run() command in the class work automatically even when nothing is calling it? there isnt a single other "run" in this code
thanks in advance
This is a typical "threading" pattern. You first subclass "Thread" and override the run() method.
After that you crate an instance of that class which you see here:
click_thread = ClickMouse(delay, button)
click_thread.start()
The "start" method launches the thread which uses the "run" function. So the line that effectively "makes the run() method run" is click_thread.start().
You can find more details in the docs of threading.Thread

How to interrupt a thread/process by making a correct "Stop" button in Tkinter (Python)?

I want some basics on the problem of making some sort of "Stop" button that in my case terminates the series of beeps:
from tkinter import *
import winsound
from random import randint
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.widgets()
def widgets(self):
self.beep = Button(self, text = "Beep", command = play_beep)
self.beep.pack()
self.stop = Button(self, text = "Stop", command = stop_beep)
self.stop.pack()
go_on = True
def play_beep():
count = 10
while go_on == True and count != 0:
winsound.Beep(randint(100, 2500), 200)
count -= 1
def stop_beep():
go_on = False
root = Tk()
app = App(root)
root.mainloop()
When I press the "Beep" button it gets stuck as well as all the GUI until the beeps end. Could anyone tell me how to fix it?
I don't use TKinter, but I believe your button press is not creating a separate thread or process. The reason why your button gets stuck is because your play_beep loop is blocking your GUI execution loop. So we use threading. The thread executes at the same time as your GUI, so you can basically do two things at once (listen for GUI events and play beep noises).
import threading
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.is_playing = False
self.pack()
self.widgets()
def widgets(self):
self.beep = Button(self, text = "Beep", command = self.play_beep)
self.beep.pack()
self.stop = Button(self, text = "Stop", command = self.stop_beep)
self.stop.pack()
def play_beep(self):
self.is_running = True
self.beep_th = threading.Thread(target=self.run)
self.beep_th.start()
def run(self):
count = 10
while self.is_running == True and count != 0:
winsound.Beep(randint(100, 2500), 200)
count -= 1
def stop_beep(self):
try:
self.is_running = False
self.beep_th.join(0)
self.beep_th = None
except (AttributeError, RuntimeError): # beep thread could be None
pass
def closeEvent(self, event): # This is a pyside method look for a TKinter equivalent.
"""When you close the App clean up the thread and close the thread properly."""
self.stop_beep()
super().closeEvent(event)
First off, your question has nothing to do about threads or processes. Tkinter is single-threaded.
If you want to run some function periodically in a tkinter program, you must give the event loop a chance to process events. The typical solution is to do it like this:
def play_beep(count=10):
if go_on and count != 0:
winsound.Beep(randint(100, 2500), 200)
root.after(1000, play_beep, count=1)
This will cause the beep to play every second (1000ms) for ten iterations. In between each call, the event loop will have a chance to process other events.
Now, if the code you are running takes a long time, you're going to have to run that code in a separate thread or process. I know nothing about winsound.Beep so I don't know if that's necessary or not.
Second, to be able to interrupt it, you need to make go_on global, otherwise you're simply setting a local variable that never gets used.
def stop_beek():
global go_on
go_on = False

Categories