2016-11-23 25 views
2

我在應用程序中使用QClipboard,可以複製和粘貼大型3D對象。粘貼操作可能會阻塞GUI一段時間,因爲大量數據必須被反序列化。如果由相同的應用程序觸發,則丟棄QClipboard :: dataChanged()信號

我想優化此對象的情況下,對象被複制和粘貼在同一個應用程序窗口。在這種情況下,我不需要全系統的剪貼板,簡單的內部函數可以存儲和複製C++對象,而無需進行反序列化。

這樣的想法是:

1)當「複製」被調用時,對象的副本存儲在內部,並且對象是序列化,並放置在系統剪貼板。設置一個標誌來記住下一個粘貼操作應該直接採用存儲的對象,而不是系統剪貼板。

2)當系統剪貼板被另一個應用程序(可能是同一個程序,但另一個進程)修改時,將設置一個標誌,以知道下一個粘貼操作應該從系統剪貼板進行反序列化。

3)「粘貼」動作檢查標誌,並採取內部存儲的對象,或反序列化系統剪貼板中的對象。

問題是1)。每當我更改系統剪貼板時,dataChanged()信號都會被觸發。這是在QClipboard :: setData被調用很長時間後異步完成的。因此,在調用setData()期間設置blockSignals()並沒有幫助。

有什麼想法?

謝謝!

回答

2

從文檔我假設QClipboard::ownsClipboard()返回true如果dataChanged()由當前過程本身造成的。

因此,您可以檢查連接到dataChanged()的插槽,例如忽略特定的調用。

1

第一個問題是,你不應該在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_copyon_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); 
    } 
}; 
+0

完全非阻塞粘貼在大多數情況下不是所需的行爲。如果粘貼操作需要很長時間,您通常希望在此期間防止用戶與GUI進行交互。帶有取消按鈕的QProgressDialog更好。 – galinette

+0

@galinette只要您不阻止事件循環,就可以向用戶提供任何行爲。如果您不讓事件循環運行以保持UI響應,那麼'QProgressDialog'就沒用了。事實上,人們經常錯誤地認爲它們不得不爲對話的'setValue'方法添加黑客攻擊:它會爲你處理任何徘徊的事件,即使這意味着可能會重新輸入一些並不意味着的代碼重新進入。很容易判斷應用程序是否保持它的事件循環響應。那些通常不會導致執行診斷快照的操作系統的額外成本(OS X!)。 –

+0

@galinette當反序列化發生在後臺時,您肯定可以顯示模態進度對話框。 –

相關問題