基于Python实现剪切板实时监控方法解析


Posted in Python onSeptember 11, 2019

前言

上网浏览网页的时候,看见好的内容免不了要使用复制粘贴,但是我们看到的内容、心里想要的内容和实际粘贴后的内容往往不一致。数据的获取始于复制,终于粘贴,那么问题来了,在这中间系统做了哪些操作,我们怎么能控制它呢?

人生苦短,我用python,查阅相关资料之后发现有很多不一样的实现方式,如利用内置ctypes模块、tk模块,第三方模块如跨平台的pyperclip模块、clipboard模块、pywin.win32clipboard模块等等,大部分都封装好了简洁易用的高级接口,方便我们直接使用。

基于强迫症的心理,本文分析比较了几种主流的方式,对他们逐一进行源码分析、读写性能实测,最后选择了读写速度最快的一种做出一个实时剪切板监控小案例,以供大家参考。

小案例实现的功能如下:

**实时监测ctrl+c剪切板写入事件,去除剪切板中指定字符或文本,如某些文字的后缀 (?_?)瞄。**

使用正则对某些文本进行智能替换,如将python2格式的代码转换为python3格式。

方式一:调用第三方pyperclip模块

In [1]: import pyperclip
In [2]: data = pyperclip.paste()
In [3]: data
Out[3]: "print 'Hello World'\r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/..."
In [4]: data = data[7:12]
In [5]: pyperclip.copy(data)
In [6]: pyperclip.paste()
Out[6]: 'Hello'

源码调用: 内置ctypes模块中的ctypes.windll.user32接口编写,和pandas包的代码一致,代码位置:pandas.io.clipboard.windows,代码引用如下

import ctypes
windll = ctypes.windll
safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
safeGetClipboardData.argtypes = [UINT]
safeGetClipboardData.restype = HANDLE
safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
safeSetClipboardData.argtypes = [UINT, HANDLE]
safeSetClipboardData.restype = HANDLE

优点: 跨平台,接口调用方便简洁

缺点: 剪切板的数据格式只支持utf-8文本,频繁读写速度较慢

方式二:调用第三方win32clipboard模块

In [1]: import win32clipboard
 ...:
 ...: def clipboard_get():
 ...: """获取剪贴板数据"""
 ...: win32clipboard.OpenClipboard()
 ...: data = win32clipboard.GetClipboardData()
 ...: win32clipboard.CloseClipboard()
 ...: return data
 ...:
 ...: def clipboard_set(data):
 ...: """设置剪贴板数据"""
 ...: win32clipboard.OpenClipboard()
 ...: win32clipboard.SetClipboardData(13, data)
 ...: win32clipboard.CloseClipboard()
 ...: return True
 ...:
In [2]: data = clipboard_get()
In [3]: data
Out[3]: "print 'Hello World'\r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上 原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/..."

In [4]: clipboard_set(data[7:12])
Out[4]: True

In [5]: clipboard_get()
Out[5]: 'Hello'

源码调用: C源码封装,python接口调用如下

def GetClipboardData(*args, **kwargs): # real signature unknown 
 pass
def SetClipboardData(*args, **kwargs): # real signature unknown
 pass

优点: 原生C封装读写速度最快,支持多种剪切板数据格式

缺点: 只适用于windows平台,高频率读写会报错需要小心处理,utf-8格式之外的数据格式需要熟悉winuser.h库自行设计编写

方法三:调用内置tkinter模块

In [1]: from tkinter import *
 ...:
 ...: r = Tk()

In [2]: data = r.clipboard_get()

In [3]: data
Out[3]: "print 'Hello World'\n————————————————\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文 出处链接及本声明。\n原文链接:https://blog.csdn.net/.../article/details/..."

In [4]: r.clipboard_append(data[7:12])

In [5]: r.clipboard_get()
Out[6]: 'Hello'

注意: 在win10系统测试后发现,使用tkinter模块只能获取剪切板数据,不能将数据写入剪切板,外部调用clipboard_board方法时,系统剪切板进程会被tk接管锁死,此时在其他的应用按ctrl+v,粘贴的应用会直接处于卡死的状态,或者粘贴后内容为空。

如果还是通过Tk()对象将数据写入剪切板,只能采取下面的方法,设置延迟销毁Tk对象,系统剪切板数据才会被更新,否则内容还是为空(实测如果设置0.2秒以内的频率读取,剪切板还是为空,这就很鸡肋了):

from tkinter import *
import time
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('some string')
r.update()
time.sleep(.2)
r.update()
r.destroy()

源码调用: C源码封装,python接口调用如下

# 读取剪切板数据:
_tkinter.tkapp('clipboard', 'get')
# 写入剪切板数据:
_tkinter.tkapp('clipboard', 'append')

剪切板读写速度测试结果

基于Python实现剪切板实时监控方法解析

实时监控小案例:

import win32clipboard
import re
import time
def clipboard_get():
  """获取剪贴板数据"""
  win32clipboard.OpenClipboard()
  data = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
  win32clipboard.CloseClipboard()
  return data
def clipboard_set(data):
  """设置剪贴板数据"""
  win32clipboard.OpenClipboard()
  win32clipboard.EmptyClipboard()
  win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT, data)
  win32clipboard.CloseClipboard()
# 初始化替换字符列表,相比于正则使用replace函数进行单字符替换更快
char_list = [('(', '('),
       (')', ')'),
       ('“', '"'),
       ('”', '"'),
       ('‘', '\''),
       (''', '\''),
       ('print ', 'print '),
       ('版权声明:本文为CSDN', '版权声明:本文为CSDN'),
       ]
# 预编译正则替换匹配表达式
# 匹配python2格式的 print函数文本
sub_print = re.compile(r'\bprint\s+(.+)')
# 匹配csdn复制自带的版权声明后缀文本
sub_csdn = re.compile(r'—+\s+版权声明:本文为CSDN.*\s+原文链接.*')
# 指定场景 sub替换函数:python2格式的 print函数 替换为python3格式
def sub_fn(s):
  return 'print(' + s.group(1).strip() + ')\r\n'
# 判断如果没有要替换的字符则返回None,有则执行替换操作,先进行字符列表replace,再执行reg.sub(sub_fn, txt)
def char_replace_reg_sub(txt):
  new_txt = txt
  # 对字符列表中字符 逐一判断,如果字符在文本中 则replace替换,如果都不在 则return None,不用再进行替换操作
  i = 0
  for old_char, new_char in char_list:
    if old_char in new_txt:
      i += 1
      new_txt = new_txt.replace(old_char, new_char)
  if i == 0:
    return None

  print('-' * 150, '\n【After char replace】:', new_txt)
  # 对指定场景替换 使用正则re.sub
  new_txt = sub_print.sub(sub_fn, new_txt)
  new_txt = sub_csdn.sub('', new_txt)
  print('【After sub replace:】', new_txt)
  return new_txt


def main():
  """后台脚本:每隔0.2秒,读取剪切板文本,检查有无指定字符或字符串,如果有则执行替换"""
  # recent_txt 存放最近一次剪切板文本,初始化值只多执行一次paste函数读取和替换
  recent_txt = clipboard_get()
  replaced_txt = char_replace_reg_sub(recent_txt)
  clipboard_set(recent_txt if replaced_txt is None else replaced_txt)

  while True:
    # txt 存放当前剪切板文本
    txt = clipboard_get()

    # 剪切板内容和上一次对比如有变动,再进行内容判断,判断后如果发现有指定字符在其中的话,再执行替换
    if txt != recent_txt:
      # print(f'txt:{txt}')
      new_txt = char_replace_reg_sub(txt) # 没查到要替换的子串,返回None

      if new_txt is not None:
        clipboard_set(new_txt)
        # 更新 recent_txt 为替换之后的文本,便于下次与 txt 剪切板文本对比,判断内容有无更新
        recent_txt = new_txt
    # 检测间隔(延迟0.2秒)
    time.sleep(0.2)
if __name__ == '__main__':
  main()

运行效果:

-----------------------------------------------------------------------------------------
【Copy text】:

print 'Hello World' \r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上 原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/...)
-----------------------------------------------------------------------------------------
【After replace:】:

print('Hello World')
-----------------------------------------------------------------------------------------

参考链接:

微软开发文档:https://docs.microsoft.com/zh-cn/windows/win32/dataxchg/using-the-clipboard?redirectedfrom=MSDN#_win32_Copying_Information_to_the_Clipboard

Stack Overflow:https://stackoverflow.com/questions/579687/how-do-i-copy-a-string-to-the-clipboard-on-windows-using-python

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
django1.8使用表单上传文件的实现方法
Nov 04 Python
python 字符串转列表 list 出现\ufeff的解决方法
Jun 22 Python
Python通过属性手段实现只允许调用一次的示例讲解
Apr 21 Python
win10系统下Anaconda3安装配置方法图文教程
Sep 19 Python
python得到电脑的开机时间方法
Oct 15 Python
解决python测试opencv时imread导致的错误问题
Jan 26 Python
Python符号计算之实现函数极限的方法
Jul 15 Python
linux环境下Django的安装配置详解
Jul 22 Python
TensorFlow实现批量归一化操作的示例
Apr 22 Python
python爬虫实现POST request payload形式的请求
Apr 30 Python
python Socket网络编程实现C/S模式和P2P
Jun 22 Python
Python爬虫防封ip的一些技巧
Aug 06 Python
python连接、操作mongodb数据库的方法实例详解
Sep 11 #Python
面向对象学习之pygame坦克大战
Sep 11 #Python
Python整数与Numpy数据溢出问题解决
Sep 11 #Python
python中通过selenium简单操作及元素定位知识点总结
Sep 10 #Python
用Python画一个LinkinPark的logo代码实例
Sep 10 #Python
Pytorch修改ResNet模型全连接层进行直接训练实例
Sep 10 #Python
django drf框架自带的路由及最简化的视图
Sep 10 #Python
You might like
php文件上传、下载和删除示例
2020/08/28 PHP
YII框架中搜索分页jQuery写法详解
2016/12/19 PHP
yii2使用GridView实现数据全选及批量删除按钮示例
2017/03/01 PHP
Gird事件机制初级读本
2007/03/10 Javascript
Display SQL Server Login Mode
2007/06/21 Javascript
Jquery 滑入滑出效果实现代码
2010/03/27 Javascript
js 手机号码合法性验证代码集合
2012/09/29 Javascript
javaScript函数中执行C#代码中的函数方法总结
2013/08/07 Javascript
jquery解析xml字符串简单示例
2014/04/11 Javascript
自定义jquery模态窗口插件无法在顶层窗口显示问题
2014/05/29 Javascript
JavaScript对数字的判断与处理实例分析
2015/02/02 Javascript
JavaScript+CSS实现仿Mootools竖排弹性动画菜单效果
2015/10/14 Javascript
JavaScript知识点总结之如何提高性能
2016/01/15 Javascript
AngularJs实现分页功能不带省略号的代码
2016/05/30 Javascript
分享jQuery网页元素拖拽插件
2020/12/01 Javascript
深入解析Javascript闭包的功能及实现方法
2016/07/10 Javascript
基于jQuery实现的打字机效果
2017/01/16 Javascript
js实现多行文本框统计剩余字数功能
2017/03/28 Javascript
React Native使用Modal自定义分享界面的示例代码
2017/10/31 Javascript
简单介绍react redux的中间件的使用
2018/04/06 Javascript
vue代码分割的实现(codesplit)
2018/11/13 Javascript
基于javascript的拖拽类封装详解
2019/04/19 Javascript
JavaScript图片旋转效果实现方法详解
2020/06/28 Javascript
Python 异常处理实例详解
2014/03/12 Python
python版微信跳一跳游戏辅助
2018/01/11 Python
Python爬虫实现(伪)球迷速成
2018/06/10 Python
python3.6根据m3u8下载mp4视频
2019/06/17 Python
解决pytorch报错:AssertionError: Invalid device id的问题
2020/01/10 Python
如何删除一个表里面的重复行
2013/07/13 面试题
LINUX下线程,GDI类的解释
2016/12/14 面试题
委托书范本
2014/09/13 职场文书
群众路线四风问题整改措施
2014/09/27 职场文书
2015年教师节贺卡寄语
2015/03/24 职场文书
2015年办公室文秘工作总结
2015/04/30 职场文书
校园广播稿范文
2015/08/19 职场文书
2016年端午节校园广播稿
2015/12/18 职场文书