背景
本文基于PyQt5,监听系统的粘贴板。当拷贝文件时,将路径提取出来显示到界面上。当黏贴的内容和文件中含有时间戳时,将其提取出来。利用这种方式解决选择某类文件只能通过浏览文件系统这种笨重方式效率不高,也不方便的问题。文中的方案在Windows平台正常,但在Mac上当复制PyQt应用程序内时监听有效,当复制其他应用程序里的内容时监听无效,这可能和Mac的沙盒模型有关系。下面先上源码,一共包含三个文件,main文件是入口。
源码
main.py
# coding=utf-8
import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QClipboard
from widgets import MainWidget
import Utils
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init()
self.addClipbordListener()
def addClipbordListener(self):
clipboard = QApplication.clipboard()
clipboard.dataChanged.connect(self.widget.onClipboradChanged)
def init(self):
self.text = QTextEdit('主窗口')
self.widget = MainWidget()
self.setCentralWidget(self.widget)
self.setGeometry(200, 200, 800, 400)
self.setWindowTitle('监听粘贴板')
self.show()
def main():
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
def test():
timestramp = Utils.extractTime('2017-6-12 12:24:56')
times = time.localtime(timestramp)
now_time = time.strftime("%Y-%m-%d %H:%M:%S", times)
print(now_time)
# 入口
if __name__ == '__main__':
test()
Utils.py
# coding=utf-8
import time
import os
import re
def isPng(path):
return str(path).endswith('.png') or str(path).endswith('.PNG')
def isVideo(path):
return str(path).endswith('.mp4')
def isAllowConvert2Time(path):
return isPng(path) or isVideo(path)
def fileName(path):
return os.path.basename(path)
def extractTimeFromFile(path):
name = fileName(path)
return extractTime(name)
def extractTime(content):
timestr = ''
timeformat = ''
patterns = [r'(\d{8}[-]\d{6})', r'(\d{8}[_]\d{6})', r'(\d{4}-\d{1,2}-\d{2}\s\d{2}:\d{2}:\d{2})', r'(\d{4}/\d{1,2}/\d{2}\s\d{2}:\d{2}:\d{2})']
formats = ['%Y%m%d-%H%M%S', '%Y%m%d_%H%M%S', '%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S']
pf = zip(patterns, formats)
for (p, f) in pf:
mat = re.search(p, content)
if mat:
timestr = mat.group()
timeformat = f
break
if timestr and timeformat:
timeArray = time.strptime(timestr, timeformat)
timestamp = time.mktime(timeArray) # 转出来的单位是秒
print(timestamp)
return timestamp
else:
return -1
# 把秒时间戳转字符串时间
def convertSeconds2TimeStr(timestamp):
times = time.localtime(timestamp)
timestr = time.strftime("%Y-%m-%d %H:%M:%S", times)
return timestr
widgets.py
# coding=utf-8
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget, QWidget, QLineEdit, QDateTimeEdit, QVBoxLayout, QHBoxLayout \
, QGridLayout, QLabel
from PyQt5.QtCore import Qt, QDateTime
import Utils
import os
# 应用程序的主Widget
class MainWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
vlayout = QVBoxLayout()
grid = QGridLayout()
label1 = QLabel('文件路径')
self.lineEdit = QLineEdit()
label2 = QLabel('时间点')
self.dateEdit = QDateTimeEdit()
self.dateEdit.setDisplayFormat('yyyy-MM-dd hh:mm:ss')
grid.addWidget(label1, 0, 0)
grid.addWidget(self.lineEdit, 0, 1, )
grid.addWidget(label2, 1, 0)
grid.addWidget(self.dateEdit, 1, 1)
vlayout.addLayout(grid)
vlayout.addStretch(1)
self.setLayout(vlayout)
def onClipboradChanged(self):
clipboard = QApplication.clipboard()
text = clipboard.text()
if text:
content = str(text)
print('onClipboradChanged---' + content + ' len = ' + str(len(content)))
if content.startswith('file:///'):
path = content.replace('file:///', '')
if not os.path.exists(path):
return
name = Utils.fileName(path)
self.lineEdit.setText(path)
if Utils.isAllowConvert2Time(name):
self.updateDateTimeEdit(filename=name)
pass
elif len(content) < 50:
self.updateDateTimeEdit(content=content)
def updateDateTimeEdit(self, filename = '', content = ''):
if filename:
times = Utils.extractTimeFromFile(filename)
elif content:
times = Utils.extractTime(content)
if times == -1:
return
timestr = Utils.convertSeconds2TimeStr(times)
print(timestr)
self.dateEdit.setDateTime(QDateTime.fromString(timestr, 'yyyy-MM-dd hh:mm:ss'))
pass
运行效果
源码要点
1.如何监听系统的粘贴板?
在PyQt5里监听系统的粘贴板还是很方便的,尤其是我需要的只是粘贴的文件路径。通过以下代码监听:
def addClipbordListener(self):
clipboard = QApplication.clipboard()
clipboard.dataChanged.connect(self.widget.onClipboradChanged)
这样当粘贴板内容发生变化时,回到到MainWidget的onClipboradChanged()
函数,在这个函数里通过下面:
def onClipboradChanged(self):
clipboard = QApplication.clipboard()
text = clipboard.text()
...
这样就能获得粘贴的文本内容,当复制的是文件时,这里传来的是file:\\\
开头的完整文件路径。
2.如何提取时间
上面的代码里限定里只有当复制图片和视频时才从文件名里提取时间戳。当复制的是纯文本时,如果字符串不长则提取,否则不提取,避免复制的大段文字让桌面程序一直阻塞在提取时间戳上。
具体提取请看extractTime()
函数。由于提取完时间后,还要转成时间戳,和时间的格式紧密相关。因此我写了两个数组用来存正则条件和时间戳格式。通过zip()
函数关联起来,然后遍历。只要能匹配成功,就直接break
掉。支持的时间格式现在有四种,其中前两种是手机截图和录视频时的文件命名方式:
1. 20170802-200825
2. 20170802_200825
3. 2017-08-02 20:08:25
4. 2017/08/02 20:08:25
当然还有其中时间戳格式,需要的话可以模仿然后加上去。需要注意的是,我代码里的正则表达式并不是很严谨,没有限制的太死,只是作个大概的提取够用就行。
3.时间戳如何相互转化?
由于提取出的字符串类型时间格式有很多,所以我统一先将各种形式的字符串转成时间戳。默认的Python转出来的是以秒为单位的,然后将其再转成一个固定的字符串格式的时间,将其设到QDateTimeEdit控件上。
字符串转时间戳
通过下面代码:
timeArray = time.strptime(timestr, timeformat)
timestamp = time.mktime(timeArray) # 转出来的单位是秒
核心是通过strptime()
函数,第一个参数是字符串,第二个参数是时间格式,得到一个元组格式的时间。通过调用mktime()
函数,将时间元组转成时间戳.
时间戳转字符串时间
通过下面代码进行:
times = time.localtime(timestamp)
timestr = time.strftime("%Y-%m-%d %H:%M:%S", times)
通过time.localtime()
输入的是时间戳,得到的是时间元组。然后通过strftime()
将时间元组转成对应字符串格式。
4.QDateTimeEidt控件使用
使用这个控件是一定要设置显示的格式:setDisplayFormat('yyyy-MM-dd hh:mm:ss')
如果给定字符串时间要设给它通过:self.dateEdit.setDateTime(QDateTime.fromString(timestr, 'yyyy-MM-dd hh:mm:ss'))
即可。
本文系原创,转载请注明作者yanzi1225627