Как отказаться от QEvent вместо того, чтобы просто игнорировать его

У меня есть два виджета ParentWidget а также ChildWidget оба получены из QWidget и оба имеют приоритет void dragEnterEvent(QDragEnterEvent *event),

Сейчас ChildWidget содержится в ParentWidget, Теперь предположим, что определенный QDragEvent* называется event может быть действительным для ParentWidget, но не для ChildWidget и предположим, что dragEnterEvent за ChildWidget называется.

Теперь я могу просто позвонить event->ignore() чтобы игнорировать событие для ChildWidget, но потом dragEnterEvent за ParentWidget называется.

И это моя проблема. Я не хочу, чтобы dragEnterEvent за ParentWidget вызывается, если событие уже было удалено в ChildWidget,

Проще говоря, я просто не хочу, чтобы событие просто игнорировалось, но, кроме того, событие должно быть полностью отброшено внутри dragEnterEvent из ChildWidget,

Как можно добиться такого поведения в предположении, что ParentWidget а также ChildWidget такое слабосвязанные компоненты?

Минимальный пример

Следующий пример показывает, чего я пытаюсь достичь, и в некотором смысле это также осуществимый подход. В случае более сложных сценариев это приведет к чрезмерно сложному коду.

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();
}
}
};

1

Решение

Если вы хотите, чтобы событие было отменено, вам нужно принимать Это:

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();
}
3

Другие решения

Ваше решение — достойный обходной путь.

Кроме того, вы можете изменить тип события на событие без перетаскивания. Поскольку событие перестает быть QDragEnterEvent, он не будет отправлен родителю. Есть два способа реализовать это: один — изменить t (тип) член QEvent, Другой способ — уничтожить событие на месте и воссоздать обычное нулевое событие.

// https://github.com/KubaO/stackoverflown/tree/master/questions/event-discard-43885834
#include <QtWidgets>

void wipeEvent(QEvent * event) {
struct Helper : QEvent {
static void wipe(QEvent * e) {
static_cast<Helper*>(e)->t = QEvent::None;
}
};
Helper::wipe(event);
}

void wipeEvent2(QEvent *event) {
event->~QEvent(); // OK since the destructor is virtual.
new (event) QEvent(QEvent::None);
}

class ChildWidget : public QWidget {
Q_OBJECT
QHBoxLayout m_layout{this};
QLabel m_label{"ChildLabel"};
public:
ChildWidget() {
setAcceptDrops(true);
m_layout.addWidget(&m_label);
}
void dragEnterEvent(QDragEnterEvent *event) override {
qDebug() << "Child";
while (auto mimeData=event->mimeData()) {
auto url = QUrl(mimeData->text());
if (!url.isValid()) break;
if (!url.isLocalFile()) break;
auto filename = url.fileName();
if (!filename.endsWith(".txt")) break;
// ChildWidget can only process txt files.
qDebug() << url.fileName();
return event->acceptProposedAction();
}
wipeEvent(event);
}
};

class ParentWidget : public QWidget {
Q_OBJECT
QHBoxLayout m_layout{this};
QLabel m_label{"ParentLabel"};
ChildWidget m_child;
public:
ParentWidget() {
setAcceptDrops(true);
m_layout.addWidget(&m_label);
m_layout.addWidget(&m_child);
}
void dragEnterEvent(QDragEnterEvent *event) override {
qDebug() << "Parent";
event->acceptProposedAction();
}
};

int main(int argc, char** args) {
QApplication app{argc, args};
ParentWidget widget;
widget.show();
app.exec();
}
#include "main.moc"
0

После вчерашнего долгого разговора я нашел лучшее решение моей проблемы. Решение похоже на решение Бенджамина Т. Еще раз большое спасибо ThibautB. для плодотворного обсуждения.

Вот мой рабочий код.

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();
}

ChildWidget.h

#pragma once

#include <QWidget>
#include <QUrl>
#include <QMimeData>
#include <QDragEnterEvent>
#include <QLabel>
#include <QDebug>
#include <QByteArray>
#include <QHBoxLayout>
//#include "MyDragEnterEvent.h"
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) {
qDebug() << "Child";
if (auto mimeData=event->mimeData()) {
auto url = QUrl(mimeData->text());
if (!url.isValid()) { event->setDropAction(Qt::DropAction::IgnoreAction); event->ignore(); return; }
if (!url.isLocalFile()) { event->setDropAction(Qt::DropAction::IgnoreAction); event->ignore(); return; }
auto filename = url.fileName();
if (!filename.endsWith(".txt")) { event->setDropAction(Qt::DropAction::IgnoreAction); event->ignore(); return; }
// ChildWidget can only process txt files.
qDebug() << url.fileName();
event->acceptProposedAction();
}
else {
qDebug() << "Ignored in Child";
event->setDropAction(Qt::DropAction::IgnoreAction);
event->ignore();
}
}
};

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 {
if (event->dropAction() == Qt::IgnoreAction) {
qDebug() << "Ignored in Parent";
event->ignore();
}
else {
qDebug() << "Accepted in Parent";
event->acceptProposedAction();
}
}
};
0
По вопросам рекламы [email protected]