检测python中的粘贴

检测python中的粘贴,python,tkinter,clipboard,Python,Tkinter,Clipboard,我想检测用户何时在任何应用程序中粘贴了内容,这样我就可以将一个新项目复制到剪贴板中(用例:我有一个从数据库中逐个复制到网页中的项目列表,并希望在粘贴完成后自动将下一个项目放入剪贴板。) 目前,我有一个使用Tkinter的按钮,当使用以下代码按下时,它会复制一个字段 self.root.clipboard_clear() self.root.clipboard_append(text) 然后,我需要某种方法来检测何时在另一个应用程序中执行了粘贴,这样我就可以将下一个项目加载到剪贴板中。我希望它能

我想检测用户何时在任何应用程序中粘贴了内容,这样我就可以将一个新项目复制到剪贴板中(用例:我有一个从数据库中逐个复制到网页中的项目列表,并希望在粘贴完成后自动将下一个项目放入剪贴板。)

目前,我有一个使用Tkinter的按钮,当使用以下代码按下时,它会复制一个字段

self.root.clipboard_clear()
self.root.clipboard_append(text)
然后,我需要某种方法来检测何时在另一个应用程序中执行了粘贴,这样我就可以将下一个项目加载到剪贴板中。我希望它能在Win/Mac/Linux上工作,就像我在这三个平台上工作一样。有什么想法吗

免责声明:我不是剪贴簿专家。这个答案是我对它们如何工作的理解。这可能是完全错误的

几乎没有特定于平台的方法来解决这个问题,更不用说跨平台的方法了。从剪贴板粘贴包含两个断开连接的步骤:

  • 偷看/阅读剪贴板上的内容
  • 以特定于应用程序的方式使用该数据
  • 在第二步中,应用程序可能会检查从剪贴板读取的数据类型,如果与活动上下文中可以粘贴的数据类型不匹配(例如,无法在纯文本编辑器中粘贴图像),则可能会忽略该数据类型。如果粘贴发生,它会发生在用户空间中,每个应用程序可能会以不同的方式进行粘贴。在所有平台下检测所有可能的实现根本没有任何意义

    充其量,您可以监视偷看剪贴板内容的行为,但是任何应用程序(考虑第三方剪贴板管理器)都可以在用户没有任何明确操作的情况下急切地检查剪贴板,因此这些事件之后不一定会有任何可观察到的数据利用率


    以下来自现实世界的松散类比可能会说服你放弃寻找解决方案。假设你申请并被授予某个配方的专利。专利已经公布,任何人都可以阅读。您能否寻求一种有效的方法来检测根据专利配方烹饪的任何菜肴实例?

    在ms windows上,您似乎应该能够使用(使用)截获相关的WM_粘贴消息。

    正如所指出的,在标准条件下,一旦将复制的对象释放到野外,就不太可能检测到它们的任何使用。然而,大多数现代操作系统都支持所谓的“延迟渲染”。不仅可以在主机和目标之间协商选择的格式,而且不建议在不知道内存的去向的情况下复制大块内存。Windows和X都提供了一种方法,可以通过这种机制完全实现您想要的功能

    与其详细介绍每个操作系统如何实现其剪贴板API,不如看一个相当标准的跨平台包:。剪贴板访问是通过类实现的。可以通过避免使用方便的方法和使用来触发延迟渲染。特别是,您将创建一个自定义子类,该子类实现根据请求获取数据,而不仅仅是将数据存储在剪贴板中。您还必须设置自己的和的实现,这应该不是问题。这将允许您根据请求动态协商可用格式,这是延迟渲染通常实现的方式

    现在让我们来看一个小的应用程序。您在问题中提到,您有一个项目列表,在第一个项目被复制后,您希望依次复制这些项目。我们就这么做吧。我们的自定义

    retrieveData
    实现将当前选择转换为字符串,用UTF-8或其他编码方式对其进行编码,将选择向前移动,并将其复制到剪贴板:

    from PyQt5.QtCore import Qt, QMimeData, QStringListModel, QTimer, QVariant
    from PyQt5.QtGui import QClipboard
    from PyQt5.QtWidgets import QAbstractItemView, QApplication, QListView
    
    class MyMimeData(QMimeData):
        FORMATS = {'text/plain'}
    
        def __init__(self, item, hook=None):
            super().__init__()
            self.item = item
            self.hook = hook
    
        def hasFormat(self, fmt):
            return fmt in self.FORMATS
    
        def formats(self):
            # Ensure copy
            return list(self.FORMATS)
    
        def retrieveData(self, mime, type):
            if self.hasFormat(mime):
                if self.hook:
                    self.hook()
                return self.item
            return QVariant()
    
    class MyListView(QListView):
        def keyPressEvent(self, event):
            if event.key() == Qt.Key_C and event.modifiers() & Qt.ControlModifier:
                self.copy()
            else:
                super().keyPressEvent(event)
    
        def nextRow(self):
            current = self.selectedIndexes()[0]
            row = None
            if current:
                row = self.model().index(current.row() + 1, current.column())
            if row is None or row.row() == -1:
                row = self.model().index(0, current.column())
            self.setCurrentIndex(row)
            QTimer.singleShot(1, self.copy)
    
        def copy(self, row=None):
            if row is None:
                row = self.selectedIndexes()[0]
            data = MyMimeData(row.data(), self.nextRow)
            QApplication.clipboard().setMimeData(data, QClipboard.Clipboard)
    
    model = QStringListModel([
        "First", "Second", "Third", "Fourth", "Fifth",
        "Sixth", "Seventh", "Eighth", "Ninth", "Tenth",
    ])
    
    app = QApplication([])
    
    view = MyListView()
    view.setSelectionMode(QAbstractItemView.SingleSelection)
    view.setModel(model)
    view.show()
    
    app.exec_()
    
    这里的QTimer对象只是一个黑客,可以快速获得一个单独的线程来运行副本。尝试在Qt空间外复制会引发一些与线程相关的问题

    这里的关键是,您不能简单地将文本复制到剪贴板,而是创建一个占位符对象。您将无法使用简单的界面,例如,并且很可能

    好的一面是,上面的示例希望向您展示PyQt5对于一个简单的应用程序来说并不太复杂(对于非简单的应用程序来说,这绝对是一个不错的选择)。同样令人高兴的是,大多数操作系统都支持某种形式的延迟渲染,Qt可以采用这种形式


    请记住,延迟渲染只能让您知道某个应用程序何时从剪贴板读取对象,而不一定是您想要的对象。事实上,它不必是“粘贴”:应用程序可能只是在偷看剪贴板。对于问题中描述的简单操作,这可能很好。如果您想更好地控制通信,请使用更高级的工具,如特定于操作系统的对谁读取复制的数据的监控,或更健壮的解决方案,如共享内存。

    好的,我曾经使用它为我的工具制作全局热键。(在中,它还支持linux/mac OS。)

    一个简单的例子:

    from pynput.keyboard import GlobalHotKeys
    import platform
    
    # platform.system() can detect the device.(macOS/Windows/Linux)
    
    def yourfunction():
        print("detect paste...") # each time when you pressed "Ctrl+V",it will call the function
    
    if platform.system() == 'Windows':
        with GlobalHotKeys({"<ctrl>+v":yourfunction}) as listener:
            listener.join()
    
    从pynput.keyboard导入全局热键
    导入平台
    #platform.system()可以检测设备。(macOS/Windows/Linux)
    def yourfunction():
    打印(“检测粘贴…”)#每次按“Ctrl+V”键时,它都会调用该函数
    如果platform.system()=“Windows”:
    使用全局热键({“+v”:yourfunction})作为侦听器:
    listener.join()
    


    PS:它可以用在Chrome、Edge(大多数应用程序)中。但它不能用在游戏中。

    我不知道你是否能轻易地检测到其他应用程序的粘贴,但你肯定能够定期检查剪贴板是否已更改。@Nae我曾想过这样做,但因为这是我的应用程序将东西添加到剪贴板中,它不会特别改变,因为里面的东西不会在粘贴时被移除它会跟踪环境的变化