1.背景
之前使用mac办公,alfred里有个剪切板功能非常好用,因为工作原因需要使用window办公,网上寻找替代品功能上有点不近人意,没有剪切板功能十分难受,于是决定自己做一个
2.技术选型
当然是使用Qt来做,但是不会C++会Python就用PyQt来实现
3.需求
- 系统托盘
- 窗口失去焦点自动最小化托盘
- 快捷键唤起
- 输入框补全提示
- 补全提示如果有直接展示
- 键盘down键直接选择提示文字回车之前进行粘贴
- pyQt打包成exe文件后多次打开exe程序只存活一个应用程序
4.实现
一、构建QT界面
class ClipboardViewer(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.center()
self.line_edit = QLineEdit(self)
self.setWindowOpacity(0.9)
self.line_edit.setStyleSheet("""
QLineEdit{
border: 1px solid #555555; /* 边框宽度为1px,颜色为#A0A0A0 */
border-radius: 5px; /* 边框圆角 */
padding-left: 5px; /* 文本距离左边界有5px */
background-color: #faf9f3; /* 背景颜色 */
color: black; /* 文本颜色 */
selection-background-color: #A0A0A0; /* 选中文本的背景颜色 */
selection-color: #F2F2F2; /* 选中文本的颜色 */
font-family: "Microsoft YaHei"; /* 文本字体族 */
}
""")
font_size = 40
self.line_edit.setFixedSize(400, font_size)
# 根据文本长度调整字体大小
font = self.line_edit.font()
font.setPointSize(font_size - 25)
self.line_edit.setFont(font)
#唤起时窗口保持在所有其他窗口的顶部
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
#窗口无边框和标题栏
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
layout = QVBoxLayout()
layout.setSpacing(0)
layout.addWidget(self.line_edit)
self.setLayout(layout)
def center(self):
# 获取屏幕的几何信息
screen = QDesktopWidget().screenGeometry()
# 获取窗口的几何信息
window_size = self.geometry()
# 计算居中的位置
x = (screen.width() - window_size.width()) / 2
y = (screen.height() - window_size.height()) / 2
# 设置窗口的位置
self.move(x, y)
只构建了一个文本框用于搜索剪切板的内容
二、实现功能
系统托盘
class ClipboardViewerApp(QApplication):
def __init__(self, sys_argv):
super().__init__(sys_argv)
self.setApplicationName("剪切板内容查看器")
self.viewer = ClipboardViewer()
# 设置系统托盘图标和菜单
self.tray_icon = QSystemTrayIcon(QIcon(":/favicon.ico"), self)
self.viewer.setWindowIcon(QIcon(":/favicon.ico"))
self.tray_icon.setToolTip("剪切板内容查看器")
# 添加右键菜单
self.menu = QMenu()
exit_action = QAction("退出", self)
# 退出事件
exit_action.triggered.connect(self.quit)
self.tray_icon.setContextMenu(self.menu)
self.tray_icon.activated.connect(self.on_tray_icon_activated)
self.tray_icon.show()
窗口失去焦点自动最小化托盘
class ClipboardViewer(QWidget):
# 控制展示标识
hidden_to_tray = True
#信号
sig_show_viewer = pyqtSignal()
def __init__(self):
super().__init__()
self.initUI()
def event(self, event):
# 窗口事件失去焦点
if event.type() == QEvent.WindowDeactivate:
self.sig_show_viewer.emit()
return super().event(event)
class ClipboardViewerApp(QApplication):
def __init__(self, sys_argv):
# ...代码省略
self.viewer.sig_show_viewer.connect(self.showViewer)
def showViewer(self):
if self.viewer.hidden_to_tray:
self.viewer.show()
self.viewer.activateWindow()
self.viewer.hidden_to_tray = False
else:
self.viewer.hide()
self.viewer.hidden_to_tray = True
快捷键唤起关闭
(不能跟系统快捷键冲突 我这边设置的alt+x ps:mac的commond键按习惯了。。)
class ClipboardViewerApp(QApplication):
#热键信号
sigkeyhot = pyqtSignal()
def __init__(self, sys_argv):
#。。。代码省略
# 设置我们的自定义热键响应函数-窗口展示
self.sigkeyhot.connect(self.showViewer)
# 初始化热键
self.hk_start = SystemHotkey()
# 绑定快捷键和对应的信号发送函数
self.hk_start.register(('alt', 'x'), callback=lambda x: self.sigkeyhot.emit())
输入框补全提示 和 补全提示如果有直接展示
输入框提示可以使用QComboBox来做 但是对于cv工程师来说有这么一个场景我复制的一个字符串,然后我在复制另一个字符串,这时候我想使用第一次复制的字符串,我唤起剪切板窗口就能直接找到我要的文本,QComboBox必须输入提示词才能展示出来,一直找不到让他一直展示的属性,就自己实现了下这个功能
class ClipboardViewer(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#。。代码省略
# 设置一个列表框让他充当提示框
self.list_widget = QListWidget()
# 设置显示的选项数
self.list_widget.setStyleSheet("""QListWidget{
border:1px solid gray; color:black;
border-radius: 5px;
border: 1px solid #555555;
background-color: #faf9f3; /* 背景颜色 */
hover:background:skyblue;
}
}
""")
layout.addWidget(self.list_widget)
class ClipboardViewerApp(QApplication):
def __init__(self, sys_argv):
#让列表无值的时候不显示有值的话一直显示
self.updateVisibility()
clipboard = self.clipboard()
# 监听剪切板变动
clipboard.dataChanged.connect(self.change_deal)
def updateVisibility(self):
if self.viewer.list_widget.count() == 0:
self.viewer.list_widget.setVisible(False)
else:
self.viewer.list_widget.setVisible(True)
def change_deal(self):
data = self.clipboard().mimeData()
text = data.text()
text = str(text)
text = text.strip()
# 判断文本是否存在历史记录里有就清掉
if text in paseList:
paseList.remove(text)
# 最多20条历史记录
if len(paseList) > 20:
temp = paseList
temp = temp[:19]
paseList.clear()
paseList.extend(temp)
paseList.insert(0, text)
# 每次清空列表在放进去
self.viewer.list_widget.clear()
self.viewer.list_widget.addItems(paseList)
self.updateVisibility()
if __name__ == '__main__':
#将复制的内容放在这个列表中
paseList = []
键盘down键直接选择提示文字回车之前进行粘贴
class ClipboardViewer(QWidget):
enterHot = pyqtSignal()
#窗口中键盘事件
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Down:
# 按键时列表聚焦
if self.list_widget.count() > 0:
a = self.list_widget.currentIndex()
index = a.row()
index += 1
if index == self.list_widget.count():
index = 0
self.list_widget.setCurrentRow(index)
if event.key() == QtCore.Qt.Key_Up:
# 按键时列表聚焦
if self.list_widget.count() > 0:
a = self.list_widget.currentIndex()
index = a.row()
index -= 1
if index < 0:
index = self.list_widget.count()-1
if index == self.list_widget.count():
index = 0
self.list_widget.setCurrentRow(index)
if event.key() == QtCore.Qt.Key_Return:
# 回车时发送信号
self.enterHot.emit()
return super().keyPressEvent(event)
class ClipboardViewerApp(QApplication):
def __init__(self, sys_argv):
#接收回车信号
self.viewer.enterHot.connect(self.enterHotEvent)
def enterHotEvent(self):
date = self.viewer.list_widget.currentIndex().data()
self.showViewer()
# 模拟键盘按键组合
pyperclip.copy(date)
pyautogui.hotkey('ctrl', 'v') # 模拟复制操作
pyQt打包成exe文件后多次打开exe程序只存活一个应用程序
if __name__ == '__main__':
paseList = []
try:
serverName = 'pasteServer'
socket = QLocalSocket()
socket.connectToServer(serverName)
# 如果连接成功,表明server已经存在,当前已有实例在运行
if socket.waitForConnected(500):
pass
else:
app = ClipboardViewerApp(sys.argv)
localServer = QLocalServer() # 没有实例运行,创建服务器
localServer.listen(serverName)
localServer.newConnection.connect(app.showViewer)
# 处理其他
sys.exit(app.exec_())
except:
pass
三、界面展示效果:
四、总结
界面有点丑O(∩_∩)O哈哈~ 主打一个能用就行 这期间也学到了许多关于Qt的知识 以后有机会继续坐点小工具