如何丢弃QEvent而不是忽略它

时间:2017-05-10 07:16:05

标签: c++ qt drag-and-drop drag qevent

我有两个小部件ParentWidgetChildWidget都来自QWidget并且都覆盖void dragEnterEvent(QDragEnterEvent *event)

现在ChildWidget包含ParentWidget。现在假设某个名为QDragEvent*的{​​{1}}可能对event有效,但对ParentWidget无效,并假设ChildWidget的{​​{1}}被调用。

现在我可以调用dragEnterEvent来忽略ChildWidget的事件,但调用event->ignore()的{​​{1}}。

这是我的问题。如果事件已在ChildWidget中丢弃,我不想要dragEnterEvent ParentWidget被调用。

简单地说,我只是不想忽略该事件,而且事件需要在dragEnterEvent ParentWidget内完全丢弃。

如果假设ChildWidgetdragEnterEvent是松散耦合的组件,怎么能实现这样的行为呢?

最小示例

以下示例显示了我想要实现的目标,并且在某种意义上也是一种可行的方法。如果出现更复杂的情况,则会导致代码过于复杂。

ChildWidget接受以ParentWidget结尾的文件名,而ChildWidget接受所有丢弃,但ChildWidget已忽略的除外。

的main.cpp

txt

ParentWidget.h

ParentWidget

ChildWidget.h

ChildWidget

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

您的解决方案是一个不错的解决方法。

或者,您可以将事件类型更改为非拖动事件。由于事件不再是t,因此不会将其分派给父母。有两种方法可以实现它:一种是更改QEvent的{​​{1}}(类型)成员。另一个是就地破坏事件并在那里重新创建一个普通的null事件。

// 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"