第一個問題是,你不應該在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);
}
};
完全非阻塞粘貼在大多數情況下不是所需的行爲。如果粘貼操作需要很長時間,您通常希望在此期間防止用戶與GUI進行交互。帶有取消按鈕的QProgressDialog更好。 – galinette
@galinette只要您不阻止事件循環,就可以向用戶提供任何行爲。如果您不讓事件循環運行以保持UI響應,那麼'QProgressDialog'就沒用了。事實上,人們經常錯誤地認爲它們不得不爲對話的'setValue'方法添加黑客攻擊:它會爲你處理任何徘徊的事件,即使這意味着可能會重新輸入一些並不意味着的代碼重新進入。很容易判斷應用程序是否保持它的事件循環響應。那些通常不會導致執行診斷快照的操作系統的額外成本(OS X!)。 –
@galinette當反序列化發生在後臺時,您肯定可以顯示模態進度對話框。 –