Having trouble quitting sched.scheduler module? - python

I'm using Selenium Webdriver in my program in order to try and automate something. I am then parsing th resulting page, and checking for a specific element in the page. If the page doesn't have the specific element, then I use sched.scheduler to re-automate the task, by having the user click a button (in the Tkinter GUI). The button runs a function, which schedules a task for sched.scheduler, and has the task be sent to a function in which I created a new process from the multiprocessing module.
This is basically what it is:
import time
import sched
from multiprocessing import Process
#the function needs to run for the first time, then waits for user input if an error shows up
#if it's the second time around, the worker function runs the scheduler
global first_time_happening
first_time_happening = True
terminate = False
scheduler = sched.scheduler(time.time, time.sleep)
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
scheduler.run()
def process():
p = Process(target=worker)
#p.daemon = True
p.start()
def timer():
if not terminate:
global relay_to_process
relay_to_process = scheduler.enter(5, 2, process)
scheduler.run()
if terminate:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
def quit_button():
global terminate
terminate = True
if scheduler.empty:
print("The line is empty")
elif not scheduler.empty:
print("Something in the queue!")
while not scheduler.empty:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
worker()
#simulating where the GUI asks a question, person presses a button, and the button redirects them
#to function worker()
worker()
#simulating a user press the quit button
quit_button()
It keeps running even after I "hit" quit (or call the quit function in this case). I keep getting the queue is empty, but I'm not sure why it isn't working? Any help is appreciated, thanks!!

The scheduler keeps running even with an empty queue just in case somebody (presumably another thread) entered something again. I believe the way to make it end is to raise an exception (whether from the action or delay function) -- .run will propagate it and you can catch it.
To wit...
class AllDoneException(Exception): pass
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
try:
scheduler.run()
except AllDoneException:
pass
and in function timer
if terminate:
raise AllDoneException

Related

How to stop this running threading.Thread?

I found this non blocking code on stack overflow which is using threads to provide functionality of nonblocking setInterval function in JavaScript. But when I try to stop the process it doesn't even the Ctrl + C is not stopping it, I have tried some more methods too stop the process but they are not working.
Can someone please tell a right way to stop the process, thank you in advance.
here is the code
import threading
class ThreadJob(threading.Thread):
def __init__(self,callback,event,interval):
'''runs the callback function after interval seconds
:param callback: callback function to invoke
:param event: external event for controlling the update operation
:param interval: time in seconds after which are required to fire the callback
:type callback: function
:type interval: int
'''
self.callback = callback
self.event = event
self.interval = interval
super(ThreadJob,self).__init__()
def run(self):
while not self.event.wait(self.interval):
self.callback()
event = threading.Event()
def foo():
print ("hello")
def boo():
print ("fello")
def run():
try:
k = ThreadJob(foo,event,2)
d = ThreadJob(boo,event,6)
k.start()
d.start()
while 1:
falg = input("Press q to quit")
if(falg == 'q'):
quit()
return
except KeyboardInterrupt:
print('Stoping the script...')
except Exception as e:
print(e)
run()
print( "It is non-blocking")
All you need to do is to replace this line:
quit()
with this one:
event.set()
A Python program won't exit if there are threads still running, so the quit() function wasn't doing anything. The threads will exit their while loops once the event's internal flag is set, so a call to event.set() will cause the termination of both extra threads you created. Then the program will exit.
Note: technically you could set the threads to be "daemon" and then they will not keep the program alive. But that's not the right solution here, I think.

Hotkey to stop script - Python

Am trying to create a hotkey to stop my script, this is my code so far.
import time
import keyboard
running = True
def stop(event):
global running
running = False
print("stop")
# press ctrl+esc to stop the script
keyboard.add_hotkey("ctrl+esc", lambda: stop)
while running:
time.sleep(2)
print("Hello")
time.sleep(2)
add_hotkey expects a callback as the second argument, so you must pass it the stop function, on the other hand, when the callback is invoked, no event is passed.
A better solution than using a boolean variable is to use threading.Event since this is thread-safe since the callback is invoked in a secondary thread.
import threading
import time
import keyboard
event = threading.Event()
def stop():
event.set()
print("stop")
keyboard.add_hotkey("ctrl+esc", stop)
while not event.is_set():
time.sleep(2)
print("Hello")
time.sleep(2)

Python 3 Autoclicker On/Off Hotkey

I'm new to Python and I figured I'd make a simple autoclicker as a cool starter project.
I want a user to be able to specify a click interval and then turn the automatic clicking on and off with a hotkey.
I am aware of Ctrl-C, and you'll see that in my current code, but I want the program to work so that the hotkey doesn't have to be activated in the python window.
import pyautogui, sys
print("Press Ctrl + C to quit.")
interval = float(input("Please give me an interval for between clicks in seconds: "))
try:
while True:
pyautogui.click()
except KeyboardInterrupt:
print("\n")
Do I need to make a tkinter message box to make the switch or can I use a hotkey?
Thanks for the help.
Update
import multiprocessing
import time
import pyHook, pyautogui, pythoncom
import queue
click_interval = float(input("Please give an interval between clicks in seconds: "))
class AutoClicker(multiprocessing.Process):
def __init__(self, queue, interval):
multiprocessing.Process.__init__(self)
self.queue = queue
self.click_interval = click_interval
def run(self):
while True:
try:
task = self.queue.get(block=False)
if task == "Start":
print("Clicking...")
pyautogui.click(interval == click_interval)
except task == "Exit":
print("Exiting")
self.queue.task_done()
break
return
def OnKeyboardEvent(event):
key = event.Key
if key == "F3":
print("Starting auto clicker")
# Start consumers
queue.put("Start")
queue.join
elif key == "F4":
print("Stopping auto clicker")
# Add exit message to queue
queue.put("Exit")
# Wait for all of the tasks to finish
queue.join()
# return True to pass the event to other handlers
return True
if __name__ == '__main__':
# Establish communication queues
queue = multiprocessing.JoinableQueue()
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()
AutoClicker.run(self)
First of all if you want to monitor global input outside the Python window you will need pyHook or something similar. It will allow you to monitor keyboard events. I chose to act upon the F3 and F4 key-presses, which are used for starting and stopping the autoclicker.
To accomplish what you've asked, the best way I'm aware of is to create a process which will do the clicking, and to communicate with it through the use of a Queue. When the F4 key is pressed, it will add an "Exit" string to the queue. The autoclicker will recognize this and then return.
Before the F4 key has been pressed, the queue will remain empty and the queue.Empty exception will continually occur. This will execute a single mouse click.
import multiprocessing
import time
import pyHook, pyautogui, pythoncom
import queue
class AutoClicker(multiprocessing.Process):
def __init__(self, queue, interval):
multiprocessing.Process.__init__(self)
self.queue = queue
self.click_interval = interval
def run(self):
while True:
try:
task = self.queue.get(block=False)
if task == "Exit":
print("Exiting")
self.queue.task_done()
break
except queue.Empty:
time.sleep(self.click_interval)
print("Clicking...")
pyautogui.click()
return
def OnKeyboardEvent(event):
key = event.Key
if key == "F3":
print("Starting auto clicker")
# Start consumers
clicker = AutoClicker(queue, 0.1)
clicker.start()
elif key == "F4":
print("Stopping auto clicker")
# Add exit message to queue
queue.put("Exit")
# Wait for all of the tasks to finish
queue.join()
# return True to pass the event to other handlers
return True
if __name__ == '__main__':
# Establish communication queues
queue = multiprocessing.JoinableQueue()
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()
Keep in mind this implementation is far from perfect, but hopefully it's helpful as a starting point.
Why can't I use a simple while loop or if/else statement?
A while loop is blocking, which means when the loop is running it blocks all other code from executing.
So once the clicker loop starts it will be stuck in that loop indefinitely. You can't check for F4 key presses while this is happening because the loop will block any other code from executing (ie. the code that checks for key-presses). Since we want the auto-clicker clicks and the key-press checks to occur simultaneously, they need to be separate processes (so they don't block each other).
The main process which checks for key presses needs to be able to communicate somehow with the auto-clicker process. So when the F4 key is pressed we want the clicking process to exit. Using a queue is one way to communicate (there are others). The auto-clicker can continuously check the queue from inside the while loop. When we want to stop the clicking we can add an "Exit" string to the queue from the main process. The next time the clicker process reads the queue it will see this and break.

python thread weird behavior

I have a timer function which I am calling it in another function like this
import time
import threading
def f():
while(True):
print "hello"
time.sleep(5)
def execute():
t = threading.Timer(5,f)
t.start()
command = ''
while command != 'exit':
command = raw_input()
if command == 'exit':
t.cancel()
Even if after entering "exit" command, the function is printing "hello"
I am not able to figure out Whats wrong with the code
class threading.Timer - cancel() - Doc-Link
Stop the timer, and cancel the execution of the timer’s action. This will only work if the timer is still in its waiting stage.
A very simple Version of what you are trying to accomplish could look like this.
import threading
_f_got_killed = threading.Event()
def f():
while(True):
print "hello"
_f_got_killed.wait(5)
if _f_got_killed.is_set():
break
def execute():
t = threading.Timer(5,f)
t.start()
command = ''
while command != 'exit':
command = raw_input()
if command == 'exit':
_f_got_killed.set()
t.cancel()
execute()
For forcefully killing a thread look at this:
Is there any way to kill a Thread in Python?
You are using cancel wrong. In http://docs.python.org/2/library/threading.html, it states: "Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling the cancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user."
In your code, if you try to use cancel after the timed thread has already begun its execution (it will in 5 seconds), cancel accomplishes nothing. The thread will remain in the while loop in f forever until you give it some sort of forced interrupt. So typing "exit" in the first 5 seconds after you run execute works. It will successfully stop the timer before the thread even begins. But after your timer stops and your thread starts executing the code in f, there will be no way to stop it through cancel.

Multiprocessing beside a main loop

I'm struggling with a issue for some time now.
I'm building a little script which uses a main loop. This is a process that needs some attention from the users. The user responds on the steps and than some magic happens with use of some functions
Beside this I want to spawn another process which monitors the computer system for some specific events like pressing specif keys. If these events occur then it will launch the same functions as when the user gives in the right values.
So I need to make two processes:
-The main loop (which allows user interaction)
-The background "event scanner", which searches for specific events and then reacts on it.
I try this by launching a main loop and a daemon multiprocessing process. The problem is that when I launch the background process it starts, but after that I does not launch the main loop.
I simplified everything a little to make it more clear:
import multiprocessing, sys, time
def main_loop():
while 1:
input = input('What kind of food do you like?')
print(input)
def test():
while 1:
time.sleep(1)
print('this should run in the background')
if __name__ == '__main__':
try:
print('hello!')
mProcess = multiprocessing.Process(target=test())
mProcess.daemon = True
mProcess.start()
#after starting main loop does not start while it prints out the test loop fine.
main_loop()
except:
sys.exit(0)
You should do
mProcess = multiprocessing.Process(target=test)
instead of
mProcess = multiprocessing.Process(target=test())
Your code actually calls test in the parent process, and that call never returns.
You can use the locking synchronization to have a better control over your program's flow. Curiously, the input function raise an EOF error, but I'm sure you can find a workaround.
import multiprocessing, sys, time
def main_loop(l):
time.sleep(4)
l.acquire()
# raise an EOFError, I don't know why .
#_input = input('What kind of food do you like?')
print(" raw input at 4 sec ")
l.release()
return
def test(l):
i=0
while i<8:
time.sleep(1)
l.acquire()
print('this should run in the background : ', i+1, 'sec')
l.release()
i+=1
return
if __name__ == '__main__':
lock = multiprocessing.Lock()
#try:
print('hello!')
mProcess = multiprocessing.Process(target=test, args = (lock, ) ).start()
inputProcess = multiprocessing.Process(target=main_loop, args = (lock,)).start()
#except:
#sys.exit(0)

Categories

Resources