Завершение программы в середине

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()
Другие вопросы по тегам