Threading when excluding subprocess - python

I wrote a program that catches keyboard events on tetris, which was opened via a subprocess. no when i only want to catch events without opening Tetris my keyboard handler (pyHook) won't catch the events:
# open Tetris
#Tetris = subprocess.Popen (["C:\\Program Files (x86)\Tetris\Tetris.exe"])
#start game by sending "F2" key
#for hwnd in get_hwnds_for_pid (Tetris.pid):
#win32gui.PostMessage (hwnd, win32con.WM_KEYDOWN, win32con.VK_F2, 0)
keyH = KeyHandler()
ToneT = ToneTimer()
keyH.setDaemon(True)
ToneT.setDaemon(True)
keyH.start()
ToneT.start()
this worked fine when using another subprocess but i do not know why this is stuck now. KeyHandler won't pick up the keys.
class KeyHandler(threading.Thread):
def run(self):
# watch for all keyboard events
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
all but the last statement gets printed, but evoked keys are not printed, but instead when i press a key the app freezes (I have a function to catch events that works fine with the subprocess included...)
I guess there is something wrong with the threading when leaving out the subprocess.
I found out that if I switch to another GUI window before pressing a key, and then press a key, keys get accepted and keyevents printed.
More precise info: This probably has to do with running the script inside of OpenSesame (experimental software) as somehow pyhook does not retrieve keyboard responses from his window / windows in general but only from gui windows?! Thus I might need to seek an alternative for pyhook here ?

It's not working due to Qt bypassing the windows message loop -- pyHook relies on the message loop to function.
Modern graphical interface frameworks, such as Windows Forms, Windows
Presentation Foundation, MFC, Delphi, Qt and others do not
typically require applications to directly access the Windows message
loop, but instead automatically route events such as key presses and
mouse clicks to their appropriate handlers as defined within the
framework.
From Message Loop in MS Windows

The run method needs to have a loop. The thread ends once it leaves the run method.
class KeyHandler(threading.Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.running = True
# end
def run(self):
while self.running:
# watch for all keyboard events
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
# end run
or You don't have to subclass it at all.
def catch_key(keyboard):
# watch for all keyboard events
KeyHandler.hm.KeyDown = keyboard.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
thread = threading.Thread(target=catch_key, args=(Keyboard()))
thread.start()
# Remember to use thread.join() to safely close the thread.

Related

Call python function with hotkey

I would like to call a python function or a python file to execute with a hotkey. My function is an infinite loop. I would like to terminate the execution as well with a hotkey. Is it possible?
Windows shortcuts is not an option, because I would like to choose 1 key to execute and not a combination of keys ( for example CRTL + SHIFT + L ) .
It would be nice if the hotkey would be the middle ( scroll ) button of the mouse.
You can setup a hotkey using the mouse module (pip install mouse). To run a function in a loop and then stop in on the hotkey again, you will need to create a new thread (intro to threading) that will run this loop. Then you will want to use an event to stop this thread's execution when the hotkey is pressed again. Below is an example of an implementation that does this.
import mouse # pip install mouse
import threading
import time
def your_function():
# the function you want to execute in the loop
print("Executing your function...")
time.sleep(0.9) # You will probably want to remove this
def repeat_function(kill_event):
print("Starting loop...")
while not kill_event.is_set():
your_function()
print("Exiting loop...")
while True:
print("Waiting for hotkey press...")
kill_event = threading.Event()
new_thread = threading.Thread(target=lambda: repeat_function(kill_event))
# set the hotkey that will start the loop to middle mouse button click
start_hook = mouse.on_middle_click(new_thread.start)
mouse.wait(mouse.MIDDLE, mouse.UP) # wait till the hotkey gets released
mouse.unhook(start_hook) # clear the old hotkey
# set the hotkey to kill the loop instead
end_hook = mouse.on_middle_click(kill_event.set)
mouse.wait(mouse.MIDDLE, mouse.UP)
new_thread.join() # wait for thread to fully finish, unnecessary
mouse.unhook(end_hook) # remove kill hotkey and start again

How to use multi-threading to play/pause BG Music in tkinter app?

A part of what I am doing, needs me to have background music in a tkinter game I created long time ago. I am using playsound.playsound{ Docs Here } to play music . I could use any other tool if needed to achieve what I intend like pyglet.media or pygame.mixer.
As the actual program was about 1k lines, I have tried adding an MCVE below.
Desired behavior & issue
The BGM (Background Music) should start when the app is launched - of course alongside a GUI, I would have a button to stop the BGM OR more preferably pause/play - I do think I need to use anything other than playsound.playsound for pause/play behavior.
The issue:: I can't seem to figure out how to make that communication between both the threads so that I can stop the music from playing or perhaps terminate the thread playing BGM - I could create a new thread when needed.
What I Have Tried
First up, I created two classes for GUI and BGM, each inheriting from threading.Thread - overridden the constructor and run() methods to do what I intend. Like this -
import tkinter as tk
import threading
from playsound import playsound
class BGM(threading.Thread):
def __init__(self, stopSignal):
threading.Thread.__init__(self)
self.stopSignal = stopSignal
def run(self):
while not self.stopSignal:
playsound('LoL.mp3') # to make this function play asynchronously, pass in False as second arg.
class GUI(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.root = tk.Tk()
def run(self):
tk.Button(self.root, text='stop').pack()
if __name__ == '__main__':
guiThread = GUI()
guiThread.start()
musicThread = BGM(0)
musicThread.start()
guiThread.root.mainloop()
But as you can see, it will just continue to play the song infinitely as long as the app is alive. There is no communication ( or active sync ) between GUI ( the button to stop song ) and BGM thread.
Now I tried using threading.Event() objects. The official docs on threading.Event() quotes:
This is one of the simplest mechanisms for communication between
threads: one thread signals an event and other threads wait for it.
An event object manages an internal flag that can be set to true with
the set() method and reset to false with the clear() method. The
wait() method blocks until the flag is true.
I'm not sure if this is useful for me to achieve what I intend but using threading.Event() did not do anything for me (or maybe I wasn't able to make use of it properly)
Now
What would you suggest to do to implement a BGM thread which can be stopped ( or paused/played preferably ) with a button(s) in the GUI thread.
Thank You For Any help Or Suggestions :)

Should I use <keypress> or <keyrelease> (or <Return>) for RFID scanning?

I have a Raspberry Pi and an RFID scanner running a python script. I'm using tkinter to capture input using the following code.
from Tkinter import *
import Tkinter as tk
def __init__(self):
command = tk.Tk()
self.e = Entry(command)
self.e.grid()
self.e.focus_set()
command.bind('<KeyPress>', self.key_input)
command.mainloop()
def key_input(self, event):
key_press = event.keysym
if key_press == 'Return':
time.sleep(0.5)
self.enter()
else:
pass
def enter(self):
//various API calls etc. Here is where the RFID tag is often duplicated)
I'm getting some weird behaviour where the RFID tag is captured twice before a return is fired and I'm wondering if it is because of the order of operations.
Would binding using < keypress> vs < keyrelease> change anything? Or not because it's an RFID scanning and not a user pressing keys? Would using the < Return> be preferential? Or is the above code accomplishing the same thing?
What happens is that when you press and hold the key, OS generates multiple Press and Release events in a loop, not a single Press and single Release when the key is actually finally released. So, using only one of Press or Release events won't change anything.
One possibility is it to process both Press and Release events, and track the state of the key (if key is "being pressed") in event handlers. Now, this solves nothing as is, but then the trick is to also use after_idle to defer the processing of the Release event. after_idle schedules the execution on the next event loop, and after other events are processed, so:
def __init__(self):
...
self.being_pressed = False
command.bind('<KeyPress-Return>', key_input)
command.bind('<KeyRelease-Return>', key_release)
def key_input(self, event):
if not self.being_pressed:
self.enter()
def key_release(self, event):
self.being_pressed = True
self.after_idle(self.do_release, event)
def do_release(self.event):
self.being_pressed = False
This way, you still get all Press and Release events, but because Release events are now processed in next event loop, if key is being pressed for a long time, N+1 Press event handler will be executed before the N-th Release event handler, and thus will detect that the key is still being pressed.
Optionally, you can also use after cancel in Press event handler to cancel Release event processing at all.

How to get characters input in keyboard while playing a game using python?

I am making an AI to drive cars in GTA San Andreas using Tensorflow and I want to know which characters/keys are pressed at each frame using python. I cannot use input() cause my program is not in front. What I do?
pyHook might be something you might be looking for. All the keyboard or mouse events can be captured using Windows hooks. pyhook is a Python wrapper around the hooks API.
This answer presents a sample code for using pyhook to capture the keypress. This document provides with basics of Hook in Windows.
Below is an example, which hooks for keyboard events and prints the key pressed to console. It exits on keypress for x or X.
#!python
import pythoncom, pyHook
import sys
def OnKeyboardEvent(event):
# block only the letter A, lower and uppercase
print chr(event.Ascii)
if event.Ascii in (ord('x'), ord('X')):
sys.exit()
# returning True to pass on event to other applications
return True
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()

wxPython custom SplashScreen with progress gauge

How do I create a custom splash screen in wxPython that has a loading bar and begins automatically on startup?
My answer with a code sample is below.
I'd been trying to create a custom splash screen that incorporated a progress gauge (loading bar) using wxPython that loaded automatically on start up. After some tweaking, I was finally able to create one. In order to do this, I had to override the application's MainLoop() method. Below is the Application module I used.
class Application(wx.App):
"""Inherits from wx.App and serves as the entry point for the wxPython application.
To run the program, an instance of this class is created and its method "MainLoop" is called.
"""
def __init__(self):
"""Initializes our wx.App by calling its super method. It also instantiates a new
ApplicationManager which manages the different components of the project session.
"""
wx.App.__init__(self, redirect = False)
def OnInit(self):
"""Instantiates the main frame. The OnInit method is only available for classes that derive
wx.App. Override this method to do application initialization.
"""
self.appManager = ApplicationManager()
self.appManager.applicationFrame = ApplicationFrame(self)
self.appManager.splashScreen = SplashScreenDialog()
self.keepRunning = True
return True
def MainLoop(self):
"""This function is overridden to allow the splash screen to load properly at the start
of the application. On the first loop, the method displays the splash screen and loads
the necessary files. When the application is closed, the keepRunning variable is set
to false (see ApplicationFrame) and the while loop is ended.
"""
self.loadedSplashScreen = False
# Create an event loop and make it active. If you are only going to temporarily have a nested
# event loop then you should get a reference to the old one and set it as the active event
# loop when you are done with this one...
eventLoop = wx.GUIEventLoop()
oldEventLoop = wx.EventLoop.GetActive()
wx.EventLoop.SetActive(eventLoop)
# This outer loop determines when to exit the application, for this example we let the main
# frame reset this flag when it closes.
while self.keepRunning:
# At this point in the outer loop you could do whatever you implemented your own MainLoop
# for. It should be quick and non-blocking, otherwise your GUI will freeze.
# Place splash screen events on the stack
if (not self.loadedSplashScreen):
self.appManager.splashScreen.Show(True)
# This inner loop will process any GUI events until there are no more waiting.
while eventLoop.Pending():
eventLoop.Dispatch()
if (not self.loadedSplashScreen):
for i in range(0,10000000):
# Do loading stuff here
j = float(i/100000.0)
self.appManager.splashScreen.gauge.SetValue(j)
self.appManager.splashScreen.Close()
self.appManager.applicationFrame.Show(True)
self.SetTopWindow(self.appManager.applicationFrame)
self.loadedSplashScreen = True
# Send idle events to idle handlers. This may need to be throttle back so
# there is not too much CPU time spent in the idle handlers.
time.sleep(0.10) # Throttling back
eventLoop.ProcessIdle()
wx.EventLoop.SetActive(oldEventLoop)
When you close the application, you need to set self.keepRunning to false in order for the MainLoop() method to finish. Hope this helps!

Categories