pythoncom.PumpMessages()
私が理解していることから、この行は基本的にプログラムに永遠に待つように指示しています。私の目的では、それは機能しているようです。しかし、適切な刺激を与えられれば、プログラムを終了できるようにしたいと思います。上記の行を終了するか、プログラムの実行を停止するにはどうすればよいでしょうか。
pythoncom.PumpMessages()
私が理解していることから、この行は基本的にプログラムに永遠に待つように指示しています。私の目的では、それは機能しているようです。しかし、適切な刺激を与えられれば、プログラムを終了できるようにしたいと思います。上記の行を終了するか、プログラムの実行を停止するにはどうすればよいでしょうか。
これらのドキュメントによると、pythoncom.PumpMessages()
:
WM_QUIT メッセージまで、現在のスレッドのすべてのメッセージをポンプします。
したがって、メッセージの収集を停止する 1 つの方法は、 ctypesライブラリを使用してPostQuitMessageを呼び出し、メッセージ キューに WM_QUIT メッセージを投稿することです。
ctypes.windll.user32.PostQuitMessage(0)
タイマー スレッドを使用してアプリを終了する例を次に示します。
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()
メインスレッドからのみ機能しますが、メインスレッドがブロックされているため、それ自体はあまり役に立ちません。独自のカスタム メッセージ処理をメッセージ ループにフックする場合にのみ使用できます。
GreggとBoaz Yanivによる両方の回答を拡張したいと思います。通常、ブロッキング コードは別のスレッドで実行するため、WM_QUIT をスレッドに送信する必要があります。グレッグが指摘したようにPostQuitMessageを使用する必要がありますが、それは現在のスレッドでのみ機能します。PostThreadMessage を使用してWM_QUITを送信しないでください (ドキュメントのどこにあるのか思い出せません)。詳細については、「特別な PostQuitMessage 関数があるのはなぜですか? 」のディスカッションで読むことができます。最初に WM_CLOSE をスレッドに送信する方が良いと思います。
# 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()
RegisterHotKey に使用していますが、原則は同じです。このクラスは次のように呼び出すことができます。
# 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)
メッセージの待機を終了するには、次のように呼び出します。
self.hotkey.unregister()