Seite 1 von 2

Drag & Drop zwischen QTreeWidget's

Verfasst: 20. Juni 2007 16:48
von NoRulez
Hey @all,

bin vor kurzem auf den QT Zug aufgesprungen und muss sagen es klappt soweit alles und bin sehr zufrieden damit, bis auf das Drag & Drop zwischen 2 QTreeWidget oder eben im selben QTreeWidget.

Habe auch schon in der Doku und im Forum des öfteren von diesen 4 Methoden gelesen:

Code: Alles auswählen

     void dragEnterEvent(QDragEnterEvent *event);
     void dragMoveEvent(QDragMoveEvent *event);
     void dropEvent(QDropEvent *event);
     void mousePressEvent(QMouseEvent *event);
Jedoch springt kein Breakpoint im Code an (Debug-Mode).

Im

Code: Alles auswählen

eventFilter(QObject *object, QEvent *event)
bekomme ich nur das QEvent::DragEnter - Event.

ich würde mir gerne wenn ich zum Draggen anfange einige Informationen über das Item holen und wenn danach das Drop event ausgelöst wird ebenfalls.

Kann mir da jemand helfen?

Vielen Dank im Voraus

Lg NoRulez

Verfasst: 20. Juni 2007 16:55
von upsala

Verfasst: 20. Juni 2007 17:15
von NoRulez
Die Doku habe ich schon gelesen, vielleicht habe ich was übersehen.

Wenn ich das ganze für das Hauptfenster implementiere

Code: Alles auswählen

void MyMainWindow::dragEnterEvent(QDragEnterEvent *event) {
}
bekomme ich auch dieses Event,

nur komme ich mit TreeViews nicht weiter :(

Muss ich da das komplette QTreeWidget subclassen?

Verfasst: 20. Juni 2007 18:36
von upsala
Nein, aber du must Drag & Drop in der View freischalten.

Aus der Doku, gilt natürlich auch für eine View:

Code: Alles auswählen

QListWidget *listWidget = new QListWidget(this);
 listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
 listWidget->setDragEnabled(true);
 listWidget->setAcceptDrops(true);
 listWidget->setDropIndicatorShown(true);

Verfasst: 20. Juni 2007 19:01
von NoRulez
Das habe ich ja alles freigeschalten, ich möchte nurmehr auf die DragEnter usw. reagieren können.

Im Moment kann ich die Items kopieren, ohne das ich jedoch noch eine Funktion implementiert habe. UND das ist mein Problem. Ich muss wenn ich daten per drag and drop ziehe auch im hintergrund noch andere sachen machen. diese kann ich jedoch nur wenn ich die Events abfangen kann.

Lg

Verfasst: 21. Juni 2007 07:30
von macman
NoRulez hat geschrieben:Muss ich da das komplette QTreeWidget subclassen?
Ja. Ist nicht soviel wie es aussieht und Du hast hinterher die volle Kontrolle. Mach es, Du wirst es nicht bereuen.

Verfasst: 21. Juni 2007 07:49
von @udisello
Hallo,

leite doch einfach das QTreeWidget ab und überschreibe dort deine Events, dann kannst du auch selbst festlegen. wann und wie das Drag Event startet und welche Daten übertragen werden. Nicht viel Aufwand, aber großer Nutzen.

im Header z.B.:

Code: Alles auswählen

void mousePressEvent( QMouseEvent* pEvent );
void mouseMoveEvent( QMouseEvent* pEvent );
void dropEvent( QDropEvent* pEvent );
void dragEnterEvent( QDragEnterEvent* pEvent );
void dragMoveEvent( QDragMoveEvent* pEvent );
und in deiner cpp:

Code: Alles auswählen

void YourClass::mousePressEvent( QMouseEvent* pEvent )
{
  if( pEvent->button() == Qt::LeftButton )
  {
    m_StartPoint = pEvent->pos();
  }  
  QTreeWidget::mousePressEvent( pEvent );
}

void YourClass::mouseMoveEvent( QMouseEvent* pEvent )
{
  if( pEvent->buttons() & Qt::LeftButton )
  {
    int iDistance = ( pEvent->pos() - m_StartPoint ).manhattanLength();
    if( iDistance >= QApplication::startDragDistance() )
      StartDrag();
  }
  QTreeWidget::mouseMoveEvent( pEvent );
}

void YourClass::StartDrag()
{
    QMimeData* pMimeData = new QMimeData;
    pMimeData->setText( "whatyouwant" );
    QDrag* pDrag = new QDrag( this ); 
    pDrag->setMimeData( pMimeData );
    if( pDrag->start( Qt::CopyAction ) )
      DoSomething();
}

void YourClass::dropEvent( QDropEvent* pEvent )
{
    if( pEvent->mimeData()->hasText() )
    {
      DoSomething();
      pEvent->setDropAction( Qt::CopyAction );
      pEvent->accept();
    }
}

void YourClass::dragEnterEvent( QDragEnterEvent* pEvent )
{
  //hier Bedingungen, wann Drag akzepiert wird
  pEvent->setDropAction( Qt::CopyAction );
  pEvent->accept();
}

void YourClass::dragMoveEvent( QDragMoveEvent* pEvent )
{
  //hier Bedingungen, wann Drag akzepiert wird    
  pEvent->setDropAction( Qt::CopyAction );
  pEvent->accept();
}

Verfasst: 21. Juni 2007 10:06
von NoRulez
Vielen Dank,

nur macht es einen Unterschied wenn ich die QTreeWidgets mit dem Designer erzeugt habe?
Theoretisch wird dann die Sub-Klasse ja nicht aufgerufen oder?
Würde gerne beim Designer bleiben. Aber wie gesagt bin noch neu bei QT, vielleicht geht das ja irgendwie. :roll:

Lg

Verfasst: 21. Juni 2007 10:16
von macman
Ja, das geht. Im Designer Rechtsklick auf dein TreeWidget, 'Als Platzhalter für benutzerdefinierte Klasse', und in dem Dialog gibst Du dann nur noch deinen Klassennamen und die Header-Datei an.

Verfasst: 22. Juni 2007 13:37
von NoRulez
In der Methode

Code: Alles auswählen

void dropEvent(QDropEvent* pEvent);
kann man ja mittels

Code: Alles auswählen

pEvent->source();
pEvent->target();
die Source und Target Widget's bekommen mein Problem ist jetzt nur das das Target nicht meinem wirklichen TreeWidget entspricht :(

Sprich ich übergebe im Konstruktor meine 2 TreeWidgets (tree1 und tree2)
theoretisch müsste ja eines der beiden anspringen

Code: Alles auswählen

if(pEvent->target() == tree1)
   DoSomthing1();
else if(pEvent->target() == tree2)
   DoSomthing2();
Wie finde ich am besten raus von wo nach wo ich dragge?

Lg NoRulez[/code]

Verfasst: 22. Juni 2007 14:21
von @udisello
Nur nochmal die Frage, Du erstellst dir ein Widget und legst dort die zwei TreeWidgets ( deine abgeleitete Klasse ) an? In der abgeleiteten Klasse hast Du dann z.B. das dropEvent, welches ausgelöst wird, wenn du ein Element auf das TreeWidget ziehst. Dann einfach:

Code: Alles auswählen

void YourClass::dropEvent( QDropEvent *pEvent )
{
  YourClass *pSource = qobject_cast< YourClass* >( pEvent->source() );
  if( pSource && pSource != this )
  {
    DoSomething();
    pEvent->accept();
  }
}
Wie Du siehst, hast Du dann einen Pointer auf die Quelle (pSource) und einen auf das Ziel (this).
Berichtige mich bitte wer es besser weiß, aber QDropEvent hat kein target()??? Es gibt nur
QWidget * QDrag::target () const
Returns the target of the drag and drop operation. This is the widget where the drag object was dropped.
Das kannst Du dann in der Funktion

Code: Alles auswählen

void YourClass::StartDrag()
{
    QMimeData* pMimeData = new QMimeData;
    pMimeData->setText( "whatyouwant" );
    QDrag* pDrag = new QDrag( this );
    pDrag->setMimeData( pMimeData );
    if( pDrag->start( Qt::CopyAction ) )
       DoSomething( with pDrag->target()  );
} 
abfangen

Verfasst: 22. Juni 2007 19:52
von NoRulez
Sorry meinte es eh so

Code: Alles auswählen

void YourClass::StartDrag()
{
    QMimeData* pMimeData = new QMimeData;
    pMimeData->setText( "whatyouwant" );
    QDrag* pDrag = new QDrag( this );
    pDrag->setMimeData( pMimeData );
    if( pDrag->start( Qt::CopyAction ) )
       DoSomething( with pDrag->target()  ); // pDrag->target() ist weder tree1 noch tree2
}
pDrag->target() ist weder tree1 noch tree2

ich frage das ganze so ab

Code: Alles auswählen

if(pDrag->target() == (QWidget*)this->tree1)
      BLABLABLA_1
else if(pDrag->target() == (QWidget*)this->tree2)
      BLABLABLA_2
Lg NoRulez

Verfasst: 22. Juni 2007 22:05
von NoRulez
OK ich habe das ganze nochmals angeschaut, pDrag->target() ist NULL wieso bekomme ich da nicht den Drag-Target?

ich habe für beide Tree's folgendes im Hauptfenster eingestellt:

Code: Alles auswählen

tree1->setSelectionMode(QAbstractItemView::SingleSelection);
tree1->setDragEnabled(true);
tree1->setAcceptDrops(true);
tree1->setDropIndicatorShown(true);

tree2->setSelectionMode(QAbstractItemView::SingleSelection);
tree2->setDragEnabled(true);
tree2->setAcceptDrops(true);
tree2->setDropIndicatorShown(true);
Mit QAbstractItemView::ExtendedSelection funktioniert es auch nicht

Lg NoRulez

Verfasst: 25. Juni 2007 09:18
von @udisello
Also ich glaube, wir reden noch aneinander vorbei. Du sollst eine neue Klasse erstellen, die du von QTreeWidget ableitest. Dort überschreibst du die Funktionen, die ich gepostet habe. In deinem MainWindow erstellst du dir dann zwei Objekte( tree1, tree2 ) deiner neuen Klasse. Dann brauchst du theoretisch das target() gar nicht mehr, da das dropEvent an "zwei unterschiedlichen Stellen" eintritt.

Verfasst: 26. Juni 2007 16:13
von NoRulez
So jetzt ist alles klar und funktioniert prima

Danke vielmals

Habe nur noch ein winziges Problem.

Beim Drag and Drop scheint es von haus aus so zu sein das er einen Blauen balken als Drag Image hat. Kann man den irgendwie überschreiben/zeichnen?

Lg NoRulez