检测python中的粘贴

Adm*_*onB 10 python clipboard tkinter

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

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

self.root.clipboard_clear()
self.root.clipboard_append(text)
Run Code Online (Sandbox Code Playgroud)

我需要什么方法来检测何时在另一个应用程序中执行了粘贴,以便我可以将下一个项目加载到剪贴板中。我希望它能够在 Win/Mac/Linux 上运行,因为我可以在所有这三个上工作。有任何想法吗?

Mad*_*ist 5

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

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

现在让我们看一个小应用程序。您在问题中提到,您有一个项目列表,您希望在复制第一个项目后连续复制这些项目。让我们就这么做吧。我们的自定义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_()
Run Code Online (Sandbox Code Playgroud)

这里的 QTimer 对象只是一个快速获取单独线程来运行副本的 hack。尝试在 Qt 空间之外复制会触发一些与线程相关的问题。

这里的关键是您不能简单地将文本复制到剪贴板,而是创建一个占位符对象。您将无法使用类似pyperclip、 等简单界面tkinter

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

请记住,延迟渲染只能让您知道某个应用程序(不一定是您想要的应用程序)从剪贴板读取对象的时间。事实上,它不一定是“粘贴”:应用程序可以只是查看剪贴板。对于问题中描述的简单操作,这可能没问题。如果您想更好地控制通信,请使用更高级的东西,例如特定于操作系统的监视谁读取复制的数据,或更强大的解决方案(例如共享内存)。


Leo*_*eon 1

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

几乎没有特定于平台的方法来解决这个问题,更不用说以跨平台的方式来解决这个问题了。从剪贴板粘贴的操作由两个互不相关的步骤组成:

  1. 浏览/读取剪贴板内容
  2. 以特定于应用程序的方式使用该数据

在第二步中,应用程序可以检查从剪贴板读取的数据类型,如果与可以粘贴到活动上下文中的数据类型不匹配(例如,图像不能粘贴到纯文本中),则可以忽略它。编辑)。如果发生粘贴,它会发生在用户空间中,并且每个应用程序可能会以不同的方式执行。检测所有平台下所有可能的实现根本没有任何意义。

最好的情况是您可以监视偷看剪贴板内容的行为,但是任何应用程序(考虑第三方剪贴板管理器)都可以急切地检查剪贴板,而无需用户进行任何显式操作,因此这些事件不一定会伴随任何可观察到的使用那个数据。

以下来自现实世界的松散类比可能会说服您放弃寻找解决方案。假设您申请并获得了某个食谱的专利。该专利已公开,任何人都可以阅读。您能否询问一种有效的方法来检测是否有根据专利食谱烹饪菜肴的情况?