第一个问题是您不应该在 gui 线程中反序列化。您可以同时运行反序列化。
一旦解决了这个问题,您就可以针对剪贴板在内部的情况进行优化。这可以使用标志来完成 - 不会阻塞信号。
下面简要介绍了如何解决这两个问题。首先,我们有一个 Data 类来保存数据并且复制成本很高 - 所以我们不会让它被复制:
class Data {
Q_DISABLE_COPY(Data) // presumably expensive to copy
public:
//...
QMimeData* serialize() { return new QMimeData; }
static QSharedPointer<Data> deserialize(QMimeData*);
};
Q_DECLARE_METATYPE(QSharedPointer<Data>)
那么我们需要一种克隆 mime 数据的方法:
QMimeData* clone(const QMimeData *src) {
auto dst = new QMimeData();
for (auto format : src->formats())
dst->setData(format, src->data(format));
return dst;
}
最后是一个控制器对象,它具有由操作触发的on_copy 和on_paste 方法。
跟踪分两个阶段完成:首先,副本将内部状态切换为Copied。然后,当剪贴板指示数据已更改时,状态会从Copied 转换为Ready。
最后,如果状态为Ready,on_paste 将使用内部数据执行粘贴。否则会并发反序列化,反序列化完成后进行粘贴。
paste 方法应该实现数据的实际粘贴。
class Class : public QObject {
Q_OBJECT
QSharedPointer<Data> m_data;
enum { None, Copied, Ready } m_internalCopy = None;
Q_SIGNAL void reqPaste(const QSharedPointer<Data> &);
void paste(const QSharedPointer<Data> &);
void onDataChanged() {
m_internalCopy = m_internalCopy == Copied ? Ready : None;
}
public:
Q_SLOT void on_copy() {
m_internalCopy = Copied;
QScopedPointer<QMimeData> mimeData(m_data->serialize());
QApplication::clipboard()->setMimeData(mimeData.data());
}
Q_SLOT void on_paste() {
if (m_internalCopy == Ready)
return paste(m_data);
m_internalCopy = None;
auto mimeData = clone(QApplication::clipboard()->mimeData());
QtConcurrent::run([=]{
emit reqPaste(Data::deserialize(mimeData));
delete mimeData;
});
}
Class() {
qRegisterMetaType<QSharedPointer<Data>>();
connect(QApplication::clipboard(), &QClipboard::dataChanged, this,
&Class::onDataChanged);
connect(this, &Class::reqPaste, this, &Class::paste);
}
};