【问题标题】:How to track the lifetime of a Drag/Drop operation?如何跟踪拖放操作的生命周期?
【发布时间】:2016-06-15 21:59:10
【问题描述】:

我有一个应用程序,可以让我使用 startDrag()dragEnterEvent()dropEvent() 等将项目从修改后的 QListWidget 拖放到修改后的 QLabel 上。

我现在不仅希望在拖动开始时得到通知,而且在拖动中断时(通过按ESC 或放在空白处的某个地方)得到通知。 (我的目标是发现一个隐藏的小部件,一旦拖动操作被打断,我就可以将隐藏的项目拖放到该小部件上。)

我爬取了文档,但发现任何有希望的东西 - 有人已经做到了吗?

已经有一个相当的similar question,但它没有得到有用的答案,而且它已经有两年历史了——所以也许 Qt5 引入了一些新的技巧?

【问题讨论】:

  • 这个问题有一个完全不同的意图:我想知道如何才能知道拖放操作已结束 - 通过拖放到有效目标或中止(拖放到无效区域或通过按ESC)另一个问题询问我如何避免每次调用源小部件mimeData() 方法时创建QMimeData 的新实例而导致的内存泄漏。
  • 这个误会了。
  • 你能取消这个问题的重复还是我必须编辑标题?
  • 它不再被标记为我可以看到的骗子。顺便说一句,我会将您的评论重新引入问题中以防止更多混淆,即稍微澄清问题。

标签: c++ qt drag-and-drop qt5


【解决方案1】:

在 Qt 4 直到至少 Qt 5.7 中,拖动是伪同步的。它从输入QListWidget::startDrag 开始并调用QDrag::exec(),并在QListWidget::startDrag 返回时结束。因此,您可能有:

void startDrag(Qt::DropActions supportedActions) override {
   emit dragStarted();
   QListWidget::startDrag(supportedActions); // reenters the event loop
   emit dragStopped();
}

简单易行,如果它忽略是否实际发生了拖动(它可以被过滤掉)。

但是这个实现可能会改变,实际上它应该改变:事件循环重新进入是一团糟。我们还想知道QDrag::exec() 是否真的在QDrag 实例上被调用。因此,我们需要在控件返回事件循环后(通过返回或重新进入)检查是否存在QDrag 实例。

我们可以利用QDrag 作为QAbstractItemView 的子代。拖动开始后,您可以找到实例并跟踪其生命周期。生命周期监控需要向事件循环发布一个仿函数,以应对目前的伪同步拖动实现。如果/当拖动变得完全异步时,下面的代码仍然可以工作。

// https://github.com/KubaO/stackoverflown/tree/master/questions/drag-lifetime-37846521
#include <QtWidgets>

template <typename F>
static void postToThread(F && fun, QObject * obj = qApp) {
   QObject src;
   QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun),
                    Qt::QueuedConnection);
}

struct MyListWidget : QListWidget {
   Q_SIGNAL void dragStarted();
   Q_SIGNAL void dragStopped();
   MyListWidget() {
      setDragEnabled(true);
      addItem("item1");
      addItem("item2");
   }
   void startDrag(Qt::DropActions supportedActions) override {
      postToThread([this]{
         auto drag = findChild<QDrag*>();
         if (drag) {
            emit dragStarted();
            connect(drag, &QObject::destroyed, this, &MyListWidget::dragStopped);
         }
      }, this);
      QListWidget::startDrag(supportedActions); // reenters the event loop
   }
   Q_OBJECT
};

int main(int argc, char **argv) {
   QApplication app(argc, argv);
   QWidget gui;
   QVBoxLayout layout(&gui);
   MyListWidget list;
   QLabel label;
   layout.addWidget(&list);
   layout.addWidget(&label);
   QObject::connect(&list, &MyListWidget::dragStarted, [&]{ label.setText("Drag Active"); });
   QObject::connect(&list, &MyListWidget::dragStopped, [&]{ label.clear(); });
   gui.show();
   return app.exec();
}
#include "main.moc"

【讨论】:

  • 就像stackoverflow.com/q/37846843/1668622 一样,这个答案对我不起作用。标签文本已设置但未清除..
  • @frans 相反,答案有效,因为它准确地表明了正在发生的事情——一个 Qt 错误。如果第一个代码 sn-p(更简单的startDrag)也没有表示拖动结束,那么它表明一个非常严重的错误,其中不仅QDrag 泄漏,而且事件循环多次重新进入。这将导致多个其他内存泄漏,因为deleteLater 被推迟到控件返回到最外层事件循环。不要与 Qt 错误作斗争:修复它们或报告它们。您没有做错任何事情,它应该可以工作,并且不需要解决方法。
  • @frans IOW,这肯定会跟踪QDrag 的生命周期,并突出显示即使在拖动结束后QDrag 仍然存在的错误。我已经在 Windows 上检查过,它也可以正常工作。您发现了一个与 X11 相关的 Qt 错误,看起来像。报告它。你已经有一个测试用例了。
  • 解决 Qt 错误?喜欢这个?:i.dailymail.co.uk/i/pix/2010/06/01/…我不能!我宁愿尝试复制和报告它......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-06
  • 1970-01-01
  • 2010-09-17
  • 2022-11-10
  • 2015-02-27
  • 1970-01-01
  • 2011-08-11
相关资源
最近更新 更多