如何丢弃 QEvent 而不是忽略它
Posted
技术标签:
【中文标题】如何丢弃 QEvent 而不是忽略它【英文标题】:How to discard an QEvent instead of just ignoring it 【发布时间】:2017-10-08 17:02:41 【问题描述】:我有两个小部件ParentWidget
和ChildWidget
都派生自QWidget
并且都覆盖void dragEnterEvent(QDragEnterEvent *event)
。
现在ChildWidget
包含在ParentWidget
中。现在假设某个名为event
的QDragEvent*
可能对ParentWidget
有效,但对ChildWidget
无效,并假设调用ChildWidget
的dragEnterEvent
。
现在我可以只调用event->ignore()
以忽略ChildWidget
的事件,但随后会调用dragEnterEvent
for ParentWidget
。
这是我的问题。如果事件已经在ChildWidget
中被丢弃,我不希望ParentWidget
的dragEnterEvent
被调用。
简单地说,我只是不希望事件被忽略,而且事件需要在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();
;
【问题讨论】:
你能展示你的实现吗?您可以为ChildWidget
重新实现dragEnterEvent
,因为它是虚拟的
@ThibautB.,我为两个小部件实现了dragEnterEvent
,ChildWidget
和ParentWidget
。只是 dragEnterEvent
的 ParentWidget
应该表现不同,如果事件在 dragEnterEvent
的 ChildWidget
中被忽略。
@ThibautB。 QDragEnterEvent
可以传输这个布尔值吗?
如果你使用类变量是的,我认为
@ThibautB.,我为我的问题找到了解决方法。如果事件位置有一个子小部件,我知道该事件之前已经在子小部件中被忽略了。因此我也可以在父小部件中忽略它。以下代码进入ParentWidget
的dragEnterEvent
。 if (auto childWidget = childAt(event->pos())) 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();
【讨论】:
非常感谢这个很酷的解决方案。我昨天也找到了一个非常相似的解决方案。我会在这里发布。【参考方案2】:您的解决方案是一个不错的解决方法。
或者,您可以将事件类型更改为非拖动事件。由于事件不再是QDragEnterEvent
,它不会被分派给父级。有两种实现方式:一种是更改QEvent
的t
(类型)成员。另一种是就地破坏事件并在那里重新创建一个纯空事件。
// https://github.com/KubaO/***n/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_layoutthis;
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_layoutthis;
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 appargc, args;
ParentWidget widget;
widget.show();
app.exec();
#include "main.moc"
【讨论】:
【参考方案3】:昨天经过长时间的交谈,我找到了以下更好的解决我的问题的方法。该解决方案类似于 Benjamin T 的解决方案。再次感谢 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();
;
【讨论】:
以上是关于如何丢弃 QEvent 而不是忽略它的主要内容,如果未能解决你的问题,请参考以下文章