Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果由同一应用程序触发,则丢弃QClipboard::dataChanged()信号_C++_Qt_Clipboard - Fatal编程技术网

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

C++ 如果由同一应用程序触发,则丢弃QClipboard::dataChanged()信号,c++,qt,clipboard,C++,Qt,Clipboard,我在一个应用程序中使用QClipboard,可以复制和粘贴大型3D对象。粘贴操作可能会阻塞GUI一段时间,因为大量数据必须反序列化 我想针对在同一应用程序窗口上复制和粘贴对象的常见情况对此进行优化。在这种情况下,我不需要一个全系统的剪贴板,简单的内部函数可以存储和复制C++对象而不需要反序列化。 因此,我们的想法是: 1) 调用“复制”时,对象的副本存储在内部,对象被序列化并放置在系统剪贴板上。设置一个标志,以记住下一个粘贴操作应直接获取存储的对象,而不是系统剪贴板 2) 当系统剪贴板已被另一个

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

我想针对在同一应用程序窗口上复制和粘贴对象的常见情况对此进行优化。在这种情况下,我不需要一个全系统的剪贴板,简单的内部函数可以存储和复制C++对象而不需要反序列化。 因此,我们的想法是:

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

2) 当系统剪贴板已被另一个应用程序(可能是同一个程序,但另一个进程)修改时,将设置一个标志,以知道下一个粘贴操作应通过反序列化从系统剪贴板执行

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

问题是1)。每当我更改系统剪贴板时,就会触发dataChanged()信号。这是异步完成的,在调用QClipboard::setData很久之后。因此,在调用setData()期间设置blockSignals()没有帮助

有什么想法吗


谢谢

第一个问题是您不应该在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>)
最后,一个控制器对象,它具有由操作触发的
on_copy
on_paste
方法

跟踪分两个阶段完成:首先,副本将内部状态切换为
已复制
。然后,当剪贴板指示数据已更改时,状态从
Copied
转换为
Ready

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

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);
   }
};
class类:公共QObject{
Q_对象
QSharedPointer m_数据;
枚举{None,Copied,Ready}m_internalCopy=None;
Q_信号无效重新粘贴(常数QSharedPointer&);
无效粘贴(常量QSharedPointer&);
void onDataChanged(){
m_internalCopy=m_internalCopy==已复制?准备就绪:无;
}
公众:
Q_副本上的插槽无效(){
m_internalCopy=已复制;
QScopedPointer mimeData(m_data->serialize());
QApplication::clipboard()->setMimeData(mimeData.data());
}
Q_插槽在_粘贴()上无效{
如果(m_internalCopy==就绪)
返回粘贴(m_数据);
m_internalCopy=无;
auto-mimeData=clone(QApplication::clipboard()->mimeData());
QtConcurrent::run([=]{
emit-reqPaste(数据::反序列化(mimeData));
删除mimeData;
});
}
类(){
qRegisterMetaType();
连接(QApplication::clipboard(),&QClipboard::dataChanged,此,
&类::onDataChanged);
连接(此,&Class::reqPaste,此,&Class::paste);
}
};

从文档中,我假设
QClipboard::ownsClipboard()
返回
true
如果
dataChanged()
是由当前进程本身引起的


因此,您可以在连接到
dataChanged()
的插槽中检查,例如忽略该特定调用。

在大多数情况下,完全非阻塞粘贴不是理想的行为。如果粘贴操作需要很长时间,通常需要防止用户在此期间与GUI交互。带有取消按钮的QProgressDialog要好得多。@galinette只要不阻止事件循环,就可以向用户提供任何行为。如果不让事件循环运行以保持UI响应,则
QProgressDialog
是无用的。事实上,人们经常出错,以至于他们不得不在对话框的
setValue
方法中添加一个hack:它将为您处理任何延迟事件,即使这意味着可能会重新输入一些不打算重新输入的代码。很容易判断应用程序是否保持其事件循环响应。那些通常不会导致操作系统执行诊断快照(OS X!)的额外成本的操作系统。@galinette反序列化在后台进行时,您肯定可以显示一个模式进度对话框。
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);
   }
};