C++ 如何跟踪拖放操作的生存期?

C++ 如何跟踪拖放操作的生存期?,c++,qt,drag-and-drop,qt5,C++,Qt,Drag And Drop,Qt5,我有一个应用程序,让我使用startDrag(),dragEnterEvent(),dropEvent(),将项目从修改后的QListWidget拖放到修改后的QLabel上 我现在不仅想在拖动开始时得到通知,而且想在拖动中断时得到通知(通过按ESC或将其放在空白处)。(我的目标是发现一个隐藏的小部件,一旦拖动操作被中断,我就可以在其上放置隐藏的项目。) 我浏览了文档,但发现了任何有希望的东西——有人已经完成了吗 现在已经有了一个很好的解决方案,但没有得到有用的答案,而且已经有两年了——所以也许

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

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

我浏览了文档,但发现了任何有希望的东西——有人已经完成了吗


现在已经有了一个很好的解决方案,但没有得到有用的答案,而且已经有两年了——所以也许Qt5在Qt4中引入了一些新的技巧?

直到至少Qt5.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
实例上是否真的调用了
QDrag::exec()。因此,我们需要在控件返回到事件循环(通过返回或重新输入)后检查
QDrag
实例的存在性

我们可以利用
QDrag
作为
qabstractemview
的子级。一旦开始拖动,就可以找到实例并跟踪其生存期。生命周期监视需要向事件循环发布一个函子,以处理当前的伪同步拖动实现。如果/当拖动变为完全异步时,下面的代码仍然有效

// 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"
//https://github.com/KubaO/stackoverflown/tree/master/questions/drag-lifetime-37846521
#包括
模板
静态void postToThread(F&&fun,QObject*obj=qApp){
QObject src;
QObject::connect(&src,&QObject::destroyed,obj,std::forward(乐趣),
Qt::QueuedConnection);
}
结构MyListWidget:QListWidget{
Q_信号无效拖动启动();
Q_信号无效拖阻();
MyListWidget(){
setDragEnabled(真);
补充项目(“第1项”);
补充项目(“第2项”);
}
void startDrag(Qt::DropActions supportedActions)覆盖{
postToThread([this]{
自动拖动=findChild();
如果(拖动){
发射dragStarted();
连接(拖动,&QObject::destroyed,this,&MyListWidget::dragStopped);
}
},这个);
QListWidget::startDrag(supportedActions);//重新进入事件循环
}
Q_对象
};
int main(int argc,字符**argv){
QApplication应用程序(argc、argv);
qgui;
QVBoxLayout(图形用户界面);
MyListWidget列表;
QLabel标签;
layout.addWidget(&list);
layout.addWidget(&label);
QObject::connect(&list,&MyListWidget::dragStarted,[&]{label.setText(“拖动活动”);});
连接(&list,&MyListWidget::dragstop,[&]{label.clear();});
gui.show();
返回app.exec();
}
#包括“main.moc”

这个问题有一个完全不同的意图:我想知道如何得知拖放操作已结束-通过拖放到有效目标或被中止(拖放到无效区域或按ESC
)另一个问题问我如何避免每次调用源小部件
mimeData()
方法时创建
QMimeData
的新实例所导致的内存泄漏。误解了这个问题。你能取消重复这个问题吗?或者我必须编辑标题吗?我可以看到它不再被标记为重复。顺便说一句,我会把你的评论带回到问题中,以避免更多的混淆,也就是说,稍微澄清一下问题。因为这个答案对我不适用。标签文本已设置但未清除。@frans相反,答案是有效的,因为它准确地指示了正在发生的事情-一个Qt错误。如果第一个代码段(更简单的
startDrag
)也没有发出拖动结束的信号,则表明存在一个非常严重的错误,不仅
QDrag
泄漏,而且事件循环被多次重新输入。这将导致多个其他内存泄漏,因为
deleteLater
被延迟,直到控件返回到最外层的事件循环。不要与Qt bug作斗争:修复它们或报告它们。你没有做错任何事,它应该是可以工作的,没有必要采取变通办法。@frans Low,这肯定会跟踪
QDrag
的生命周期,并突出显示一个bug,其中
QDrag
即使在拖动结束后仍然存在。我已经检查了Windows,在那里它也正常工作。您发现了一个与X11相关的Qt错误,看起来像。报告它。你已经有了一个测试用例。和一个Qt bug战斗?喜欢这个吗?:我不能!我宁愿尝试复制并报告它。。。