我有两个小部件ParentWidgetChildWidget都从QWidget派生而都覆盖void dragEnterEvent(QDragEnterEvent *event)

现在ChildWidget包含在ParentWidget中。现在,假设某个称为QDragEvent*event对于ParentWidget可能有效,但对于ChildWidget无效,并假设为dragEnterEvent调用了ChildWidget

现在,我可以仅调用event->ignore()来忽略ChildWidget的事件,但是随后会调用dragEnterEventParentWidget

这是我的问题。我不希望调用dragEnterEventParentWidget(如果该事件已在ChildWidget中丢弃)。

简而言之,我只是不希望事件被忽略,而且该事件需要被完全丢弃在dragEnterEventChildWidget内部。

ParentWidgetChildWidget是松散耦合的组件的假设下,如何实现这种行为?

最小示例

以下示例显示了我正在尝试实现的目标,并且从某种意义上讲也是一种可行的方法。在更复杂的情况下,这将导致代码过于复杂。
ChildWidget接受以txt结尾的文件名滴,而ParentWidget接受所有滴,但ChildWidget已忽略的文件除外。

main.cpp

#include <QApplication>
#include "ParentWidget.h"

int main(int argc, char** args) {
    QApplication app(argc, args);
    auto widget=new ParentWidget;
    widget->show();
    app.exec();
}

ParentWidget.h
#pragma once

#include <QWidget>
#include <QDebug>
#include <QDragEnterEvent>
#include <QHBoxLayout>
#include <QLabel>

#include "ChildWidget.h"

class ParentWidget : public QWidget {
    Q_OBJECT
public:
    ParentWidget(QWidget* parent = nullptr) : QWidget(parent) {
        setLayout(new QHBoxLayout);
        setAcceptDrops(true);
        layout()->addWidget(new QLabel("ParentLabel"));
        layout()->addWidget(new ChildWidget);
    }

    void dragEnterEvent(QDragEnterEvent *event) override {
        qDebug() << "Parent";
        // Check if event was already ignored in ChildWidget?
        if (auto childWidget = qobject_cast<ChildWidget*>(childAt(event->pos()))) {
            event->ignore();
        }
        else {
            event->acceptProposedAction();
        }
    }
};

ChildWidget.h
#pragma once

#include <QWidget>
#include <QUrl>
#include <QMimeData>
#include <QDragEnterEvent>
#include <QLabel>
#include <QDebug>
#include <QByteArray>
#include <QHBoxLayout>

class ChildWidget : public QWidget {
    Q_OBJECT
public:
    ChildWidget(QWidget* parent = nullptr) : QWidget(parent) {
        setAcceptDrops(true);
        setLayout(new QHBoxLayout);
        layout()->addWidget(new QLabel("ChildLabel"));
    }

    void dragEnterEvent(QDragEnterEvent *event) override {
        qDebug() << "Child";
        if (auto mimeData=event->mimeData()) {
            auto url = QUrl(mimeData->text());
            if (!url.isValid()) { event->ignore(); return; }
            if (!url.isLocalFile()) { event->ignore(); return; }
            auto filename = url.fileName();
            if (!filename.endsWith(".txt")) { event->ignore(); return; }
            // ChildWidget can only process txt files.
            qDebug() << url.fileName();
            event->acceptProposedAction();
        }
        else {
            event->ignore();
        }
    }
};

最佳答案

如果您希望事件被丢弃,则需要接受:

void dragEnterEvent(QDragEnterEvent *event) override {
    qDebug() << "Child";
    if (auto mimeData=event->mimeData()) {
        [...]
        event->acceptProposedAction();
    }
    else {
        event->setAction(Qt::IgnoreAction);
        event->accept();
    }
}

这就是Qt向小部件分配事件的方式:该事件从子对象传播到父对象,直到小部件接受为止。

从Qt代码:
while (w) {
    if (w->isEnabled() && w->acceptDrops()) {
        res = d->notify_helper(w, dragEvent); // calls dragEnterEvent() on w
        if (res && dragEvent->isAccepted()) {
            QDragManager::self()->setCurrentTarget(w);
             break; // The event was accepted, we break, the event will not propagate to the parent
        }
    }
    if (w->isWindow())
        break;
    dragEvent->p = w->mapToParent(dragEvent->p.toPoint());
    w = w->parentWidget();
}

关于c++ - 如何丢弃QEvent而不是仅仅忽略它,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43885834/

10-10 23:27