我有一个显示小列表的PyQt5应用程序。它允许用户复制列表项。当用户复制列表项时,它使用延迟渲染将对该项的引用放置到剪贴板上。从剪贴板粘贴项目时,它会尝试切换选择并自动将下一个项目放入剪贴板
延迟渲染第一次起作用。但是,当我试图清除或重新使用剪贴板时,会出现一个内部Qt错误,它会打印一条消息,但不会传播到Python中。这是在Windows10上发生的。虽然我正在寻找一个跨平台的解决方案(因此Qt),但我目前只对在Windows上解决这个问题感兴趣
以下是应用程序外观的概述:
当我点击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
的单个实例,并根据当前行更新它检索的内容。在这种情况下,只有第一行会被反复粘贴,因为显然,一旦检索到特定格式的值,剪贴板就会缓存该值
平台规格:
PyQt5.QtCore.QT_VERSION_STR
:5.12.5PyQt5.Qt.PYQT_VERSION_STR
:5.12.3灵感来自于我试图回答Detecting paste in python
QObject的大多数属性不是thread-safe,因此您不应该修改未在其中创建它的线程中的元素。上述内容在GUI元素中更为重要。如果要延迟,则应使用QTimer,它使用Qt eventloop实现功能:
相关问题 更多 >
编程相关推荐