最近利用pyqt5写了一个小工具,想像微信一样可以在系统托盘驻留,并具备全局热键唤出、ESC键隐藏等功能,下面利用一个简单的UI界面来记录一下实现的过程
基本思路
class Tray(QSystemTrayIcon):
def __init__(self,UI):
super(Tray,self).__init__()
self.ui = UI # 传入主程序
self.setIcon(QIcon('campaign.ico')) # 托盘图标
self.setToolTip('示例') # 鼠标点在图标上的时候显示的气泡提示
self.activated.connect(self.clickedIcon) # 点击信号
self.menu()
self.show()
def clickedIcon(self,reason):
# reason:鼠标点击托盘图标时传递的整数型信号,1表示单击右键,2表示双击左键,3表示单击左键,4表示点击中键。下面定义单击左键是弹出或隐藏界面,单击右键是弹出菜单。
if reason == 3:
self.trayClickedEvent()
elif reason ==1:
self.contextMenu()
def menu(self):
menu = QMenu()
action = QAction('退出',self,triggered=self.triggered)
menu.addAction(action)
self.setContextMenu(menu)
# 单击托盘图标,程序在隐藏和显示之间来回切换
def trayClickedEvent(self):
if self.ui.isHidden():
self.ui.setHidden(False)
if self.ui.windowState() == QtCore.Qt.WindowMinimized:
self.ui.showNormal()
self.ui.raise_()
self.ui.activateWindow()
else:
self.ui.setHidden(True)
def triggered(self):
self.deleteLater() # 删除托盘图标,无此操作的话,程序退出后托盘图标不会自动清除
qApp.quit() # 后面会重写closeEvent,所以这里换一个退出程序的命令
'''
另开一个线程用于捕捉全局热键,以防止主线程堵塞导致假死
具体逻辑如下:
1、注册一个全局热键,callback为self.start(),即启动线程;
2、线程启动后,通过run()函数发送一个信号;
3、信号对应的槽函数为具体要执行的内容,在这里的目的是隐藏|弹出后台窗口。
'''
class HotKeyThread(QThread,SystemHotkey):
trigger = pyqtSignal()
def __init__(self,UI):
self.ui = UI
super(HotKeyThread,self).__init__()
self.register(('control','w'),callback=lambda x:self.start())
self.trigger.connect(self.hotKeyEvent)
def run(self):
self.trigger.emit()
def hotKeyEvent(self):
if self.ui.isHidden():
self.ui.setHidden(False)
if self.ui.windowState() == QtCore.Qt.WindowMinimized:
self.ui.showNormal()
self.ui.raise_()
self.ui.activateWindow()
else:
self.ui.setHidden(True)
def quitThread(self):
if self.isRunning():
self.quit()
class Main(QWidget,Ui_Form):
def __init__(self):
super(Main,self).__init__()
self.setupUi(self)
self.setWindowTitle('示例')
self.setWindowIcon(QIcon('campaign.ico'))
self.tray = Tray(self)
self.hotKey = HotKeyThread(self)
# 重写键盘事件,按ESC隐藏窗口
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == QtCore.Qt.Key_Escape:
self.hide()
# 重写窗口关闭事件,关闭窗口改为隐藏窗口,这样只允许在托盘图标里去退出程序
def closeEvent(self, QCloseEvent):
QCloseEvent.ignore()
self.hide()
from PyQt5.QtWidgets import QWidget,QApplication,QSystemTrayIcon,QMenu,QAction,qApp
from PyQt5 import QtCore
from PyQt5.QtCore import QThread,pyqtSignal
import sys
from PyQt5.QtGui import QIcon
from untitled import Ui_Form
from system_hotkey import SystemHotkey
class Main(QWidget,Ui_Form):
def __init__(self):
super(Main,self).__init__()
self.setupUi(self)
self.setWindowTitle('示例')
self.setWindowIcon(QIcon('campaign.ico'))
self.tray = Tray(self)
self.hotKey = HotKeyThread(self)
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == QtCore.Qt.Key_Escape:
self.hide()
def closeEvent(self, QCloseEvent):
QCloseEvent.ignore()
self.hide()
class Tray(QSystemTrayIcon):
def __init__(self,UI):
super(Tray,self).__init__()
self.ui = UI
self.setIcon(QIcon('campaign.ico'))
self.setToolTip('示例')
self.activated.connect(self.clickedIcon)
self.menu()
self.show()
def clickedIcon(self,reason):
if reason == 3:
self.trayClickedEvent()
elif reason ==1:
self.contextMenu()
def menu(self):
menu = QMenu()
action = QAction('退出',self,triggered=self.triggered)
menu.addAction(action)
self.setContextMenu(menu)
def trayClickedEvent(self):
if self.ui.isHidden():
self.ui.setHidden(False)
if self.ui.windowState() == QtCore.Qt.WindowMinimized:
self.ui.showNormal()
self.ui.raise_()
self.ui.activateWindow()
else:
self.ui.setHidden(True)
def triggered(self):
self.deleteLater()
qApp.quit()
class HotKeyThread(QThread,SystemHotkey):
trigger = pyqtSignal()
def __init__(self,UI):
self.ui = UI
super(HotKeyThread,self).__init__()
self.register(('control','w'),callback=lambda x:self.start())
self.trigger.connect(self.hotKeyEvent)
def run(self):
self.trigger.emit()
def hotKeyEvent(self):
if self.ui.isHidden():
self.ui.setHidden(False)
if self.ui.windowState() == QtCore.Qt.WindowMinimized:
self.ui.showNormal()
self.ui.raise_()
self.ui.activateWindow()
else:
self.ui.setHidden(True)
def quitThread(self):
if self.isRunning():
self.quit()
if __name__ == '__main__':
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
demo = Main()
demo.show()
sys.exit(app.exec_())