Завершение программы в середине
pythoncom.PumpMessages()
Из того, что я понимаю, эта строка в основном говорит программе ждать вечно. Для моих целей это, кажется, работает. Тем не менее, я хотел бы иметь возможность закончить программу, учитывая правильный стимул. Как можно закончить указанную выше строку или остановить дальнейшую работу программы?
3 ответа
Согласно этим документам, pythoncom.PumpMessages()
:
Перекачивает все сообщения для текущего потока до сообщения WM_QUIT.
Поэтому одним из способов остановить сбор сообщений является отправка сообщения WM_QUIT в очередь сообщений с помощью библиотеки ctypes для вызова PostQuitMessage:
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()
будет работать только из основного потока, но опять же основной поток блокируется, поэтому он не очень полезен сам по себе. Вы можете использовать его, только если подключите свою собственную обработку сообщений к циклу сообщений.
Я хотел бы расширить оба ответа Грегга и Вооза Янива. Обычно вы запускаете код блокировки в отдельном потоке, поэтому вам необходимо отправить 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()