首页 > 技术文章 > 用Python写一个跟随鼠标运动的自定义窗口

jiuliblog-2016 2018-10-30 13:09 原文

背景:因为项目需要,要开发一个在PC上运行的应用程序,生成一个跟随鼠标运动的窗口,并且监听鼠标的点击事件,并在窗口上做相应的显示。

平台:Win7 64位 + Python27 64位

支持库:PyHook + PyQt5

 

  模块的安装这里就不多说了,网上有很多。需要注意的是,PyHook对Python3的支持不是很好,会出现卡死的问题, 还有PyQt5貌似只支持64位。废话不多说了,开始造~

 

第一步:

  先用PyQt5生成一个自定义的窗口:

  

 1 #!usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import sys
 5 from PyQt5.QtCore import *
 6 from PyQt5.QtGui import *
 7 from PyQt5.QtWidgets import *
 8 
 9 class newWindow(QWidget):
10     def __init__(self, parent=None):
11         super(newWindow, self).__init__(parent)
12         self.mypix()
13         self.setWindowFlags(Qt.FramelessWindowHint) #去除界面边框
14         self.setWindowFlags(Qt.WindowStaysOnTopHint)#窗口显示在屏幕最上方
15         self.setAttribute(Qt.WA_TranslucentBackground)#背景透明
16 
17     #显示不规则图片
18     def mypix(self):
19         self.update()
20         self.pix=QPixmap('./1.png','0',Qt.AvoidDither|Qt.ThresholdAlphaDither|Qt.ThresholdDither)
21         self.resize(self.pix.size())
22         self.setMask(self.pix.mask())
23         
24     def paintEvent(self, QPaintEvent):
25         painter=QPainter(self)
26         painter.drawPixmap(0,0,self.pix.width(),self.pix.height(),self.pix)    
27 
28 if __name__=="__main__":
29     app = QApplication(sys.argv)
30     
31     w = newWindow()
32     w.setWindowTitle('一个窗口')
33     w.show()
34     
35     sys.exit(app.exec_())

 

显示效果如下:

窗口出来了,下一步救赎让它跟随着鼠标进行移动了

 

第二步  监听鼠标事件

  

 1 #!usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import pyHook
 5 import pythoncom
 6 
 7 class MouseListener():
 8 
 9     def __init__(self):
10         
11         #新建钩子管理器
12         hookmanager = pyHook.HookManager()  
13         #注册动作回调函数
14         hookmanager.MouseLeftDown = self.onMouseLeftDown 
15         hookmanager.MouseLeftUp = self.onMouseRelease
16         hookmanager.MouseMiddleDown = self.onMouseMiddleDown
17         hookmanager.MouseMiddleUp = self.onMouseRelease
18         hookmanager.MouseRightDown = self.onMouseRightDown
19         hookmanager.MouseRightUp = self.onMouseRelease
20         hookmanager.MouseMove = self.onMouseMove
21         #钩取鼠标事件
22         hookmanager.HookMouse()
23         #推送window事件消息
24         pythoncom.PumpMessages()
25         
26         
27 
28     def onMouseLeftDown(self, event):
29         print("MouseLeft Down!")
30      
31         return True
32     
33     def onMouseMiddleDown(self, event):
34         print("MouseMiddle Down!")
35 
36         return True
37 
38     def onMouseRightDown(self, event):
39         print("MouseRight Down!")
40 
41         return True
42         
43     def onMouseRelease(self, event):
44         print("Button release!")
45         
46         return True
47 
48     def onMouseMove(self, event):
49         print("Mouse Move!")
50 
51         return True
52     
53 
54 if __name__=="__main__":
55     mouseListener = MouseListener()

 

结果如下:

 

 现在最后的问题就是如何结合这两个程序了。

因为Qt界面本身是一个死循环,而监听器里的pythoncom.PumpMessage()也是一个死循环。很自然的想法就是开启多线程。

开启多线程会出现两个问题: 1、因为是死循环,关闭窗口之后,子线程还是在运行,无法关闭。正常手段无法关闭,只能强制关闭,

              但是还是会出现问题。

             2、可能是两个死循环有影响的原因,当鼠标移动到窗口的时候会卡死。

感兴趣的可以试试,代码就不贴了,下面的写解决办法。

 

绞尽脑汁,头发都掉了一斤还是解决不了!无意中发现去掉pythoncom.PumpMessages()这个语句,程序居然可以运行。哈哈,天不绝我!!!^-^

 

 1 #!usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import pyHook
 5 import pythoncom
 6 
 7 import sys
 8 from PyQt5.QtCore import *
 9 from PyQt5.QtGui import *
10 from PyQt5.QtWidgets import *
11 
12 
13 class newWindow(QWidget):
14      def __init__(self, parent=None):
15          super(newWindow, self).__init__(parent)
16          self.mypix()
17          self.setWindowFlags(Qt.FramelessWindowHint) #去除界面边框
18          self.setWindowFlags(Qt.WindowStaysOnTopHint)#窗口显示在屏幕最上方
19          self.setAttribute(Qt.WA_TranslucentBackground)#背景透明
20          #新建钩子管理器
21          self.hookmanager = pyHook.HookManager()
22          self.hookmanager.MouseMove = self.onMouseMove
23          #钩取鼠标事件
24          self.hookmanager.HookMouse()
25  
26      #显示不规则图片
27      def mypix(self):
28          self.update()
29          self.pix=QPixmap('./1.png','0',Qt.AvoidDither|Qt.ThresholdAlphaDither|Qt.ThresholdDither)
30          self.resize(self.pix.size())
31          self.setMask(self.pix.mask())
32          
33      def paintEvent(self, QPaintEvent):
34          painter=QPainter(self)
35          painter.drawPixmap(0,0,self.pix.width(),self.pix.height(),self.pix)
36 
37 
38      def closeEvent(self, QEvent):
39         self.hookmanager.UnhookMouse()
40         sys.exit()
41 
42      def onMouseMove(self, event):
43         #print("Mouse Move!")
44         (x, y) = event.Position
45         self.move(x, y)
46         return True
47     
48 
49 if __name__=="__main__":
50     app = QApplication(sys.argv)
51      
52     w = newWindow()
53     w.setWindowTitle('一个窗口')
54     w.show()
55      
56     sys.exit(app.exec_())

OK,问题解决了,解释这么简单!(PS: 我的IDLE运行,关闭时会出现Runtime Error! 直接用命令行运行就不会出现这个问题了)

 

总结:猜测可能是Qt界面本身就是有类似于pythoncom.PumpMessages()这样功能的语句,所以前面会出现鼠标移动到界面上就出现卡死的现象。

主要还是对底层不太了解,折腾了许久。

 

 

 

 

 

 

 

 

  

 

推荐阅读