Ending a Program Mid-Run
pythoncom.PumpMessages()
From what I understand this line basically tells the program to wait f开发者_StackOverfloworever. For my purposes it seems to be working. However, I'd like to be able to end the program given the right stimulus. How would one go about ending the above line, or stopping the program from running any further.
According to these docs, pythoncom.PumpMessages()
:
Pumps all messages for the current thread until a WM_QUIT message.
So one way to stop collecting messages is by posting a WM_QUIT message to the message queue by using the ctypes library to call PostQuitMessage:
ctypes.windll.user32.PostQuitMessage(0)
Here's an example of quitting the app using a timer thread:
import win32api
import win32con
import pythoncom
from threading import Timer
main_thread_id = win32api.GetCurrentThreadId()
def on_timer():
win32api.PostThreadMessage(main_thread_id, win32con.WM_QUIT, 0, 0);
t = Timer(5.0, on_timer) # Quit after 5 seconds
t.start()
pythoncom.PumpMessages()
PostQuitMessage()
will work only from the main thread, but then again the main thread is blocked, so it's not very useful by itself. You can only use it if you hook your own custom message handling into the message loop.
I would like to extend both answers by Gregg and Boaz Yaniv. You would normally run blocking code in separate thread therefore you need to send WM_QUIT to the thread. You should use PostQuitMessage as noted by Gregg but that works only in current thread. You shouldn't use PostThreadMessage to send WM_QUIT (can't remember where I see it in docs). You can read more about it in discussion "Why is there a special PostQuitMessage function?". I think it's better to send WM_CLOSE to the thread first.
# if more hotkeys needs to be supported at the same time this class needs to be rewritten
class HotKey:
def __init__(self, modifier_key, virtual_key, callback):
self.hotkey_id = 1
# shared variable to pass thread id
self.pid = mpdummy.Value('l', 0)
# start checking hotkey press in new thread
self.process_pool = mpdummy.Pool()
self.process_pool.apply_async(HotKey.register, (self.hotkey_id, self.pid, modifier_key, virtual_key, callback, ))
self.process_pool.close()
# bind windows global hotkey
@staticmethod
def register(hotkey_id, pid, modifier_key, virtual_key, callback):
# set thread ID to shared variable
# Win API could also be used:
# ctypes.windll.Kernel32.GetCurrentThreadId()
pid.value = mpdummy.current_process().ident
# register hotkey with Win API
logging.getLogger('default').info("Registering hotkey with id " + str(hotkey_id) + " for key " + str(modifier_key) + " " + str(virtual_key))
if not ctypes.windll.user32.RegisterHotKey(None, hotkey_id, modifier_key, virtual_key):
logging.getLogger('default').info("Unable to register hotkey with id " + str(hotkey_id))
msg = ctypes.wintypes.MSG()
try:
# wait for a message - it doesn't return until some message arrives
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
# WM_HOTKEY 0x0312
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms646279(v=vs.85).aspx
if msg.message == 0x0312:
logging.getLogger('default').info("Pressed hotkey with id " + str(hotkey_id))
callback()
# WM_CLOSE
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx
elif msg.message == 0x0010:
# quit current thread
# WM_QUIT shouldn't be send with PostThreadMessageA therefore we send WM_CLOSE and quit inside thread.
# More info at:
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644945(v=vs.85).aspx
# https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453
ctypes.windll.user32.PostQuitMessage(0)
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
finally:
logging.getLogger('default').info("Unregistering hotkey for id " + str(hotkey_id))
ctypes.windll.user32.UnregisterHotKey(None, hotkey_id)
def unregister(self):
# send WM_CLOSE signal to thread checking for messages
# WM_CLOSE 0x0010
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx
ctypes.windll.user32.PostThreadMessageA(self.pid.value, 0x0010, 0, 0)
# wait for thread to finish
self.process_pool.join()
I am using it for RegisterHotKey but principle is same. This class could be called as:
# bind global hotkey for "pressing" start/split button
# MOD_ALT 0x0001
# VK_F12 0x7B
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309(v=vs.85).aspx
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731.aspx
self.hotkey = hotkey.HotKey(0x0001, 0x7B, self.special_key_pressed)
When you want to end waiting for messages call:
self.hotkey.unregister()
精彩评论