我有一个应用程序,让我可以使用QListWidget
,QLabel
,startDrag()
将项目从修改后的dragEnterEvent()
拖放到修改后的dropEvent()
,等
我现在不仅希望在拖动开始时获得通知,而且当它被中断时(通过按ESC
或在虚拟中的某处掉落) )。 (我的目标是发现一个隐藏的小部件,一旦拖动操作被中断,我就可以删除再次隐藏的项目。)
我抓住了文档,但发现了一些有希望的东西 - 有人已经完成了这个吗?
已经有一个非常similar question但它没有得到有用的答案而且已经两年了 - 所以也许Qt5引入了一些新的技巧?
答案 0 :(得分:2)
在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"