PyQt5中的顺序程序复制

2024-04-19 22:56:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个显示小列表的PyQt5应用程序。它允许用户复制列表项。当用户复制列表项时,它使用延迟渲染将对该项的引用放置到剪贴板上。从剪贴板粘贴项目时,它会尝试切换选择并自动将下一个项目放入剪贴板

延迟渲染第一次起作用。但是,当我试图清除或重新使用剪贴板时,会出现一个内部Qt错误,它会打印一条消息,但不会传播到Python中。这是在Windows10上发生的。虽然我正在寻找一个跨平台的解决方案(因此Qt),但我目前只对在Windows上解决这个问题感兴趣

以下是应用程序外观的概述:

enter image description here

当我点击Ctrl+C时,所选项目被正确复制。然后我在记事本窗口中按Ctrl+V。选定的文本粘贴得很好。然后,self.copy中的QApplication.clipboard().clear()行和QApplication.clipboard().setMimeData(data)行都“静默”失败,并出现以下打印输出:

OleSetClipboard: Failed to set mime data (NULL) on clipboard: COM error 0xffffffff800401f0 CO_E_NOTINITIALIZED (Unknown error 0x0800401f0) (The parameter is incorrect.)
OleSetClipboard: Failed to set mime data (text/plain) on clipboard: COM error 0xffffffff800401f0 CO_E_NOTINITIALIZED (Unknown error 0x0800401f0) (The parameter is incorrect.)

我相信这与Qt在引擎盖下创建的支持PyQt接口的对象的生命周期有关,但我不知道如何修复它

代码如下。我实现了一个只能处理文本的自定义QMimeData类,并调用回调来响应retreiveData。我将回调放在Timer上,以便在重新使用剪贴板之前可以返回并粘贴对象。这似乎没有什么区别:即使我更新了所选内容,粘贴操作也会正确进行,更明显的是,为什么我无法访问剪贴板获取另一个副本

from PyQt5.QtCore import Qt, QMimeData, QStringListModel, QVariant
from PyQt5.QtGui import QClipboard
from PyQt5.QtWidgets import QAbstractItemView, QApplication, QListView

from threading import Timer

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):
        return list(self.FORMATS)

    def retrieveData(self, mime, type):
        if self.hasFormat(mime):
            if self.hook:
                self.hook()
            return QVariant(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 toggleRow(self):
        current = self.selectedIndexes()[0]
        self.setCurrentIndex(self.model().index((1 - current.row()) % 2, current.column()))
        Timer(0.5, self.copy).start()

    def copy(self):
        item = self.selectedIndexes()[0].data()
        data = MyMimeData(item, self.toggleRow)
        # These are the lines that fail on the second round
        QApplication.clipboard().clear()
        QApplication.clipboard().setMimeData(data)

# Boilerplate to run the app
app = QApplication([])
model = QStringListModel(["First", "Second"])
view = MyListView()
view.setSelectionMode(QAbstractItemView.SingleSelection)
view.setModel(model)
view.show()

app.exec_()

我已经尝试延长计时器的持续时间,但这并没有改变任何事情(当然,除了延迟错误消息)。这并不奇怪,因为我预计在引擎盖下会出现一些我不知道的范围界定问题

我还尝试使用MyMimeData的单个实例,并根据当前行更新它检索的内容。在这种情况下,只有第一行会被反复粘贴,因为显然,一旦检索到特定格式的值,剪贴板就会缓存该值

平台规格:

  • 操作系统:Windows 10
  • 康达版本:康达4.8.3
  • Python版本:Python 3.7.6
  • PyQt5.QtCore.QT_VERSION_STR:5.12.5
  • PyQt5.Qt.PYQT_VERSION_STR:5.12.3

灵感来自于我试图回答Detecting paste in python


Tags: fromselfdata粘贴deferrorhookitem
1条回答
网友
1楼 · 发布于 2024-04-19 22:56:20

QObject的大多数属性不是thread-safe,因此您不应该修改未在其中创建它的线程中的元素。上述内容在GUI元素中更为重要。如果要延迟,则应使用QTimer,它使用Qt eventloop实现功能:

QtCore.QTimer.singleShot(500, self.copy)

相关问题 更多 >