pyhook和wxpython-一起使用会导致挂起

时间:2019-07-10 00:33:35

标签: python wxwidgets pyhook

每当我做几次“ wx”操作时,无论单击按钮,菜单,在文本区域内,等等,我的python应用程序都停止响应。

这似乎与我在程序其他地方使用Windows挂钩有关。当两者一起使用时,情况就会变糟。

尽管在实际应用程序的上下文之外,下面的示例可能没有多大意义,但这是我作为最小示例提出的。我竭尽所能来尝试以最小的额外复杂性诊断问题。

从工作线程中调用的第二个Windows消息泵似乎确实是实现它的因素。如果我将其注释掉,则该应用程序无法挂起...或执行任何有用的操作...钩子需要一个泵,并且我无法将其放在UI线程上,否则钩子将超时,这导致我首先这个POC ...

如果运行此程序并单击鼠标十次左右,您会发现文本块出现乱码,然后应用程序挂起。有谁知道为什么?

我正在使用

  • python 2.7.16 32位
  • pyHook 1.5.1
  • wxPython 3.0.2.0 32位(适用于python 2.7)

(更新)我也尝试了相同的结果

  • python 2.7.16 32位
  • pyHook 1.5.1
  • wxPython 4.0.6 32位(适用于python 2.7)

我们在实际的应用程序中使用它们,因为我的更高版本希望继续支持Windows XP(完全不同的对话)

main.py

import wx
from twisted.internet import wxreactor

from windows_hooks import WindowsHooksWrapper
from main_window import MainWindow


def main():
    hook_wrapper = WindowsHooksWrapper()
    hook_wrapper.start()

    app = wx.App(False)
    frame = MainWindow(None, 'Hooks Testing', hook_wrapper)

    from twisted.internet import reactor
    reactor.registerWxApp(app)
    reactor.run()

    hook_wrapper.stop()


if __name__ == "__main__":
    wxreactor.install()
    main()

windows_hooks.py

import pyHook
import threading
import pythoncom


class WindowsHooksWrapper(object):
    def __init__(self):
        self.hook_manager = None
        self.started = False
        self.thread = threading.Thread(target=self.thread_proc)
        self.window_to_publish_to = None

        print "HookWrapper created on Id {}".format(threading.current_thread().ident)

    def __del__(self):
        self.stop()

    def start(self):
        if self.started:
            self.stop()

        self.started = True
        self.thread.start()

    def stop(self):
        if not self.started:
            return

        self.started = False
        self.thread.join()

    def on_mouse_event(self, event):
        """
        Called back from pyHooks library on a mouse event
        :param event: event passed from pyHooks
        :return: True if we are to pass the event on to other hooks and the process it was intended
         for. False to consume the event.
        """

        if self.window_to_publish_to:
            from twisted.internet import reactor
            reactor.callFromThread(self.window_to_publish_to.print_to_text_box, event)
        return True

    def thread_proc(self):
        print "Thread started with Id {}".format(threading.current_thread().ident)

        # Evidently, the hook must be registered on the same thread with the windows msg pump or
        #     it will not work and no indication of error is seen
        # Also note that for exception safety, when the hook manager goes out of scope, the
        #     documentation says that it unregisters all outstanding hooks
        self.hook_manager = pyHook.HookManager()
        self.hook_manager.MouseAll = self.on_mouse_event
        self.hook_manager.HookMouse()

        while self.started:
            pythoncom.PumpMessages()

        print "Thread exiting..."

        self.hook_manager.UnhookMouse()
        self.hook_manager = None

main_window.py

import threading
import wx


class MainWindow(wx.Frame):
    def __init__(self, parent, title, hook_manager):
        wx.Frame.__init__(self, parent, title=title, size=(800, 600))
        self.hook_manager = hook_manager

        self.CreateStatusBar()

        menu_file = wx.Menu()
        menu_item_exit = menu_file.Append(wx.ID_EXIT, "E&xit", " Terminate the program")

        menu_help = wx.Menu()
        menu_item_about = menu_help.Append(wx.ID_ABOUT, "&About", " Information about this program")

        menu_bar = wx.MenuBar()
        menu_bar.Append(menu_file, "&File")
        menu_bar.Append(menu_help, "&Help")
        self.SetMenuBar(menu_bar)

        self.panel = MainPanel(self, hook_manager)

        self.Bind(wx.EVT_MENU, self.on_about, menu_item_about)
        self.Bind(wx.EVT_MENU, self.on_exit, menu_item_exit)

        self.Show(True)

    def on_about(self, e):
        dlg = wx.MessageDialog(self, "A window to test Windows Hooks", "About Test Windows Hooks",
                               wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def on_exit(self, e):
        self.Close(True)


class MainPanel(wx.Panel):
    def __init__(self, parent, hook_manager):
        self.hook_manager = hook_manager
        hook_manager.window_to_publish_to = self

        self.consuming = False

        wx.Panel.__init__(self, parent)
        self.textbox = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)

        self.horizontal = wx.BoxSizer()
        self.horizontal.Add(self.textbox, proportion=1, flag=wx.EXPAND)

        self.sizer_vertical = wx.BoxSizer(wx.VERTICAL)
        self.sizer_vertical.Add(self.horizontal, proportion=1, flag=wx.EXPAND)
        self.SetSizerAndFit(self.sizer_vertical)
        self.called_back_count = 0

    def print_to_text_box(self, event):
        self.called_back_count += 1
        print "Printing message {} on Thread with Id {}".format(self.called_back_count,
                                                                threading.current_thread().ident)
        self.textbox.AppendText('MessageName: {}\n'.format(event.MessageName))
        self.textbox.AppendText('Message: {}\n'.format(event.Message))
        self.textbox.AppendText('Time: {}\n'.format(event.Time))
        self.textbox.AppendText('Window: {}\n'.format(event.Window))
        self.textbox.AppendText('WindowName: {}\n'.format(event.WindowName))
        self.textbox.AppendText('Position: {}\n'.format(event.Position))
        self.textbox.AppendText('Wheel: {}\n'.format(event.Wheel))
        self.textbox.AppendText('Injected: {}\n'.format(event.Injected))
        self.textbox.AppendText('---\n')

我还尝试了不使用Twisted的版本,而是使用wxPostEvent和自定义事件,但是我们怀疑这可能是问题所在,所以我将其更改为使用Twisted,但这仍然没有好处。

我将稍后发布一个编辑过的列表。

0 个答案:

没有答案