我在python 2.7中编写了一个工具,用于记录用户按下键盘或鼠标按钮的次数 . 点击量将显示在屏幕左上方的一个小黑框中 . 即使其他应用程序是活动应用程序,该程序也会记录点击 .
它工作正常,除非我将鼠标移到盒子上 . 然后鼠标冻结几秒钟,之后程序再次运行 . 如果我再次将鼠标移到盒子上,鼠标会再次冻结,但这次程序崩溃了 .
我试过注释掉pumpMessages(),然后程序就可以了 . 问题看起来很像这个问题pyhook+tkinter=crash,但没有给出解决方案 .
其他答案表明,在python 2.6中使用wx和pyhook时,dll文件存在错误 . 我不知道这是否相关 .
我自己的想法是它可能与两个并行运行的事件循环有关 . 我已经读过tkinter不是线程安全的,但我看不出如何让这个程序在一个线程中运行,因为我需要运行pumpmessages()和mainlooop() .
总结一下:为什么我的程序冻结鼠标悬停?
import pythoncom, pyHook, time, ctypes, sys
from Tkinter import *
from threading import Thread
print 'Welcome to APMtool. To exit the program press delete'
## Creating input hooks
#the function called when a MouseAllButtonsUp event is called
def OnMouseUpEvent(event):
global clicks
clicks+=1
updateCounter()
return True
#the function called when a KeyUp event is called
def OnKeyUpEvent(event):
global clicks
clicks+=1
updateCounter()
if (event.KeyID == 46):
killProgram()
return True
hm = pyHook.HookManager()# create a hook manager
# watch for mouseUp and keyUp events
hm.SubscribeMouseAllButtonsUp(OnMouseUpEvent)
hm.SubscribeKeyUp(OnKeyUpEvent)
clicks = 0
hm.HookMouse()# set the hook
hm.HookKeyboard()
## Creating the window
root = Tk()
label = Label(root,text='something',background='black',foreground='grey')
label.pack(pady=0) #no space around the label
root.wm_attributes("-topmost", 1) #alway the top window
root.overrideredirect(1) #removes the 'Windows 7' box around the label
## starting a new thread to run pumMessages() and mainloop() simultaniusly
def startRootThread():
root.mainloop()
def updateCounter():
label.configure(text=clicks)
def killProgram():
ctypes.windll.user32.PostQuitMessage(0) # stops pumpMessages
root.destroy() #stops the root widget
rootThread.join()
print 'rootThread stopped'
rootThread = Thread(target=startRootThread)
rootThread.start()
pythoncom.PumpMessages() #pump messages is a infinite loop waiting for events
print 'PumpMessages stopped'
3 回答
我用多处理解决了这个问题:
主进程处理GUI(MainThread)和消耗来自第二进程的消息的线程
子进程挂钩所有鼠标/键盘事件并将它们推送到主进程(通过Queue对象)
根据Tkinter需要在主线程中运行的信息而不是在这个thred之外调用,我找到了一个解决方案:
我的问题是
PumpMessages
和mainLoop
都需要在主线程中运行 . 为了接收输入并显示Tkinter标签,我需要在运行pumpMessages
和短暂运行mainLoop
以更新显示之间切换 .为了让
mainLoop()
退出,我使用了:所以在100毫秒后
root
调用它的quit
方法并打破它自己的主循环为了打破pumpMessages,我首先找到了指向主线程的指针:
然后我使用了一个新的线程将
WM_QUIT
发送到主线程(注意PostQuitMessage(0)
只有在主线程中调用它才有效):然后可以创建一个在
pumpMessages
和_2434490之间更改的while循环,更新之间的labeltext . 在两个事件循环不再同时运行之后,我没有遇到任何问题:感谢Bryan Oakley提供有关Tkinter和Boaz Yaniv的信息,以便提供所需的信息stop pumpMessages() from a subthread
Tkinter不是设计为从主要线程以外的任何线程运行 . 将GUI放在主线程中并将调用
PumpMessages
放在一个单独的线程中可能会有所帮助 . 虽然你必须要小心,不要从另一个线程调用任何Tkinter函数(除了event_generate
除外) .