2017-05-10 2 views
1

Ich habe zwei Widgets ParentWidget und ChildWidget beide abgeleitet von QWidget und beide überschreiben void dragEnterEvent(QDragEnterEvent *event).Wie verwerfen Sie ein QEvent, anstatt es nur zu ignorieren

Jetzt ist ChildWidget im ParentWidget enthalten. Angenommen, ein bestimmter QDragEvent* namens event könnte für ParentWidget gültig sein, aber nicht für ChildWidget und angenommen, dass dragEnterEvent für ChildWidget aufgerufen wird.

Jetzt kann ich einfach event->ignore() anrufen, um das Ereignis für ChildWidget zu ignorieren, aber dann dragEnterEvent für ParentWidget heißt.

Und das ist mein Problem. Ich will nicht, dass dragEnterEvent für ParentWidget aufgerufen wird, wenn das Ereignis bereits in ChildWidget verworfen wurde.

Einfach gesagt möchte ich nicht nur das Ereignis ignoriert werden, aber darüber hinaus muss das Ereignis vollständig innerhalb der dragEnterEvent von ChildWidget verworfen werden.

Wie kann ein solches Verhalten unter der Annahme erreicht werden, dass ParentWidget und ChildWidget lose gekoppelte Komponenten sind?

Minimal Beispiel

Das folgende Beispiel zeigt, was ich versuche zu erreichen ist und auch ein brauchbarer Ansatz in einem gewissen Sinne. Im Fall von komplizierteren Szenarien würde dies zu kompliziertem Code führen.

Die ChildWidget akzeptiert Tropfen Dateinamen mit txt enden, während die ParentWidget alle Tropfen akzeptiert, außer den bereits von ChildWidget ignoriert.

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

Können Sie Ihre Implementierung zeigen? Sie können '' 'dragEnterEvent''' für' '' ChildWidget''' reimplementieren, weil es ein virtuelles –

+0

@ThibautB ist. Ich habe 'dragEnterEvent' für beide Widgets,' ChildWidget' und 'ParentWidget' implementiert. Es ist nur so, dass sich 'dragEnterEvent' für' ParentWidget' anders verhalten sollte, wenn das Ereignis in 'dragEnterEvent' von' ChildWidget' ignoriert wurde. – Aleph0

+0

@ThibautB. Kann 'QDragEnterEvent' diesen Boolean transportieren? – Aleph0

Antwort

3

Wenn Sie das Ereignis wollen verworfen werden, müssen Sie akzeptieren:

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

Dies ist, wie Qt Versand Ereignisse Widgets: das Ereignis vom Kind zum Elternteil, bis ein ausbreitet Widget akzeptiert es.

Von Qt Code:

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

Vielen Dank für diese coole Lösung. Ich habe gestern auch eine ganz ähnliche Lösung gefunden. Ich werde es hier posten. – Aleph0

0

Ihre Lösung ist ein guter Workaround.

Alternativ können Sie den Ereignistyp in ein Nicht-Zieh-Ereignis ändern. Da das Ereignis kein mehr ist, wird es nicht an das übergeordnete Element gesendet. Es gibt zwei Möglichkeiten, es zu implementieren: Eine besteht darin, das Element t (Typ) von QEvent zu ändern. Ein anderer besteht darin, das Ereignis an Ort und Stelle zu zerstören und dort ein einfaches Null-Ereignis neu zu erstellen.

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

Nach einem gestern langen Chat fand ich die folgende bessere Lösung für mein Problem. Die Lösung ähnelt der von Benjamin T. Vielen Dank nochmal an ThibautB. für die fruchtbare Diskussion.

Hier geht mein Arbeitscode.

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