如果由同一应用程序触发,则丢弃QClipboard :: dataChanged()信号

时间:2016-11-23 14:51:29

标签: c++ qt clipboard

我在应用程序中使用QClipboard,可以复制和粘贴大型3D对象。由于必须对大量数据进行反序列化,粘贴操作可能会阻止GUI一段时间。

我想针对在同一应用程序窗口中复制和粘贴对象的常见情况对此进行优化。在这种情况下,我不需要系统范围的剪贴板,简单的内部函数可以存储和复制c ++对象,而无需反序列化。

所以这个想法是:

1)当"复制"被调用,对象的副本存储在内部,对象被序列化并放置在系统剪贴板上。设置一个标志以记住下一个粘贴操作应该直接获取存储的对象,而不是系统剪贴板。

2)当系统剪贴板被另一个应用程序(可能是同一个程序,但是另一个进程)修改时,会设置一个标志,以便知道应该从系统剪贴板中进行下一个粘贴操作,并进行反序列化。

3)"粘贴"动作检查标志,并获取内部存储的对象,或从系统剪贴板反序列化对象。

问题是1)。每当我更改系统剪贴板时,都会触发dataChanged()信号。这是在调用QClipboard :: setData之后很长时间异步完成的。因此,在调用setData()期间设置blockSignals()无济于事。

有什么想法吗?

谢谢!

2 个答案:

答案 0 :(得分:3)

如果QClipboard::ownsClipboard()是由当前流程本身引起的,我会假设true会返回dataChanged()

因此,您可以在与dataChanged()相关联的广告位中查看该内容,例如忽略那个特定的调用。

答案 1 :(得分: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

最后,如果状态为on_pasteReady将使用内部数据执行粘贴。否则,它将同时反序列化,并在反序列化完成后执行粘贴。

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);
   }
};