Seite 1 von 2

QTreeWidget internal move

Verfasst: 28. Mai 2008 16:39
von pronti
Wie kann man verhindern, dass bei einem Move innerhalb des QtreeWidget ein Root Item in ein Child Item verschoben wird.
Child Items soll man aber schon zwischen den Root Items verschieben können.

Mein Codebeispiel:

mytreewidget.h

Code: Alles auswählen

#ifndef MYTREEWIDGET_H_
#define MYTREEWIDGET_H_

#include <QtGui>

class MyTreeWidget : public QTreeWidget
{
	Q_OBJECT

public:
	MyTreeWidget(QWidget *parent = 0);

	QStringList mimeTypes() const;	
	bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action); 
	Qt::DropActions supportedDropActions () const;
};

#endif /*MYTREEWIDGET_H_*/
mytreewidget.cpp

Code: Alles auswählen

#include <iostream>  
#include <QtGui>
#include "mytreewidget.h"

using namespace std;

MyTreeWidget::MyTreeWidget(QWidget *parent)
    : QTreeWidget(parent)
{
    //setRootIsDecorated(false);
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	setDragEnabled(false);
	setAcceptDrops(false);
	setDropIndicatorShown(true);
	setDragDropMode(QAbstractItemView::InternalMove);
	setDragDropOverwriteMode(false);
}

QStringList MyTreeWidget::mimeTypes () const 
{
	return model()->QAbstractItemModel::mimeTypes();
}

bool MyTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
	QModelIndex idx;
	if (parent) idx = indexFromItem(parent);
	cout << index << endl;
	if (idx.isValid()) 
	{
	    cout << "isValid" << endl;
		index=0;
	}
    return model()->QAbstractItemModel::dropMimeData(data, action , index, 0, idx);		
}

Qt::DropActions MyTreeWidget::supportedDropActions () const
{
	return Qt::CopyAction | Qt::MoveAction;
}
main.cpp

Code: Alles auswählen

#include <QtGui>
#include "mytreewidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    MyTreeWidget *treeWidget = new MyTreeWidget();
    treeWidget->setColumnCount(2);
    QStringList sHeader;
    sHeader << "Letter" << "Number";
    treeWidget->setHeaderLabels(sHeader);
    QTreeWidgetItem *rootItem = new QTreeWidgetItem(treeWidget);
    rootItem->setText(0, "A");
    rootItem->setText(1, "0");
    QTreeWidgetItem *aItem = new QTreeWidgetItem(rootItem);
    aItem->setText(0, "A");
    aItem->setText(1, "0");
    aItem = new QTreeWidgetItem(rootItem);
    aItem->setText(0, "B");
    aItem->setText(1, "1");  
    aItem = new QTreeWidgetItem(rootItem);
    aItem->setText(0, "C");
    aItem->setText(1, "2");
    rootItem = new QTreeWidgetItem(treeWidget);
    rootItem->setText(0, "B");
    rootItem->setText(1, "1");
    rootItem = new QTreeWidgetItem(treeWidget);
    rootItem->setText(0, "C");
    rootItem->setText(1, "2");
    treeWidget->setWindowTitle(QObject::tr("QTreeWidget InternalMove"));
    treeWidget->show();
    
    return a.exec();
}

Verfasst: 28. Mai 2008 19:52
von Sephral

Code: Alles auswählen

    int QTreeWidgetItem::childCount () const 
"RootItems" (Gruppen) haben children, "Childs" (Gruppenelemente) nicht.
So kannst du schauen was unter Deinem Drop-Cursor liegt.
Alternativ kannst Du die "Gruppen" beim Einlesen auch irgendwie mit einem Marker versehen und Du lässt Drops nur zu, wenn du den Marker findest auf dem Item unter der Maus. Wie Du den "Marker" gestaltest ist Deiner Kreativität überlassen, du kannst die QTreeWidgetItems ableiten oder irgendwas andere missbrauchen.


Diese Funktion könnte recht nützlich für Dich werden .-)

Code: Alles auswählen

   void QWidget::dragMoveEvent ( QDragMoveEvent * event ) 

Verfasst: 29. Mai 2008 07:17
von macman
Sephral hat geschrieben:"RootItems" (Gruppen) haben children, "Childs" (Gruppenelemente) nicht.
Sehr witzig. Natürlich können Children weitere Childs haben, das ist doch gerade der Sinn eines TreeWidgets. Children haben aber ein Item als Parent, wohingegen RootItems das TreeWidget als Parent haben.

Am sinnigsten wäre es wohl QTreeWidgetItem zu überschreiben und im dragEnterEvent die Aktion zu verbieten, wenn das Parent ein QTreeWidget ist.

Verfasst: 29. Mai 2008 08:15
von Sephral
macman hat geschrieben: Sehr witzig. Natürlich können Children weitere Childs haben, das ist doch gerade der Sinn eines TreeWidgets.
Das ist nicht witzig, lies mal den ersten Beitrag. Pronti scheint seinen Baum genau so verwenden zu wollen. So wie ich es verstanden habe möchte er Drag&Drop lediglich zwischen "Elementen in Gruppen" und "Gruppen" erlauben, um die Elemente zwischen den Gruppen zu verschieben, daher stimmt meine Beschreibung.

Dass man einen Baum beliebig verschachteln kann ist mir auch klar.

Verfasst: 29. Mai 2008 09:14
von macman
Sephral hat geschrieben:Das ist nicht witzig, lies mal den ersten Beitrag.
Hab ich, da steht nichts davon das der Baum nur zwei Ebenen hat.

Verfasst: 29. Mai 2008 09:30
von Sephral
macman hat geschrieben:
Sephral hat geschrieben:Das ist nicht witzig, lies mal den ersten Beitrag.
Hab ich, da steht nichts davon das der Baum nur zwei Ebenen hat.
Richtig, weil das auch nicht wirklich wichtig ist...

Ok, dann halt so:
Wie kann man verhindern, dass bei einem Move innerhalb des QtreeWidget ein Root Item in ein Child Item verschoben wird.
Child Items soll man aber schon zwischen den Root Items verschieben können.
* RootItem (Items die children enthalten) dürfen nicht in ChildItems (Items ohne children) verschoben werden
* ChildItems dürfen auf RootItems geschoben werden

Wobei mir Root-Items wahrscheinlich nicht das "richtige" RootItem eines Models gemeint ist, sondern einfach die erste sichtbare Ebene (die wiederum im RootItem eingehängt sind).

Unter diesen Voraussetzungen ist meine Aussage
"RootItems" (Gruppen) haben children, "Childs" (Gruppenelemente) nicht.
keinesfalls witzig, sondern ein einfacher Weg die notwendige Unterscheidung für diesen Anwendungsfall während des Drag&Drop (dragMoveEvent) vorzunehmen.

Verfasst: 29. Mai 2008 10:41
von macman
Sephral hat geschrieben:Wobei mir Root-Items wahrscheinlich nicht das "richtige" RootItem eines Models gemeint ist, sondern einfach die erste sichtbare Ebene (die wiederum im RootItem eingehängt sind).
Ein TopLevelItem, da gehe ich auch von aus.
Sephral hat geschrieben:Unter diesen Voraussetzungen ist meine Aussage
"RootItems" (Gruppen) haben children, "Childs" (Gruppenelemente) nicht.
keinesfalls witzig, sondern ein einfacher Weg die notwendige Unterscheidung für diesen Anwendungsfall während des Drag&Drop (dragMoveEvent) vorzunehmen.
Entweder reden wir aneinander vorbei oder Du gehst davon aus das Gruppen keine Untergruppen haben können. Wenn Du prüfst ob ein Item Children hat, dann weißt Du nicht auf welcher Ebene sich dieses Item befindet. Du kannst nicht davon ausgehen das es sich um ein TopLevelItem handelt, es könnte auch ein Child mit Children sein.

Um zu prüfen ob es sich um ein TopLevelItem handelt, reicht ein einfaches

Code: Alles auswählen

if (item->parent() == pTreeWidget)
    // item ist ein TopLevelItem
Ich denke er hat jetzt genügend Anregungen um es hin zu bekommen :-)

Verfasst: 29. Mai 2008 11:02
von Sephral

Code: Alles auswählen

Ich denke er hat jetzt genügend Anregungen um es hin zu bekommen Smile
Da bin ich mir auch sicher :-)

Aber um die Diskussion zwischen uns noch mal zu klären.
Wir sind von unterschiedlichen Szenarien ausgegangen die beide im Interpretationsrahmen der 2-Zeiligen Beschreibung liegen. Ohne mehr Informationen wie genau der Baum aufgebaut ist und was möglich sein soll beim Drag&Drop, kommen wir hier nicht mehr auf einen gemeinsamen Nenner, ohne das Ganze in ewig lange Posts ausarten zu lassen.

Hoffen wir einfach, dass für den Threadersteller der richtige Informations-Happen dabei war :-)

Ciao,
Sephral

Verfasst: 29. Mai 2008 13:00
von pronti
Vielen Dank für die Inputs. In meinem Fall ist es wirklich so, dass ich nur zwei Ebenen habe.

Wenn ich nun dragMoveEvent neu implementiere, wie komme ich vom Event auf das QWidgetItem.

Verfasst: 29. Mai 2008 13:08
von Sephral
Hallo,

über die die Position des Mauszeigers ( event->pos() ) kannst du das Item ausfindig machen.

Ciao,
Sephral

Verfasst: 29. Mai 2008 13:54
von pronti
Irgendwie bin ich zu blöd.

Ich verwende folgenden Code:

Code: Alles auswählen

void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
	QTreeWidgetItem *item = itemAt(event->pos());
	event->ignore();
	if (item->childCount()==0)
	{
		event->setDropAction(Qt::MoveAction);
		event->accept();
	}
}
Beim Starten bekomme ich den Fehler das der referenzierte Memory nicht gelesen werden kann.

Verfasst: 29. Mai 2008 13:58
von Sephral
Prüf' das Item mal gegen NULL bevor Du es verwendest...

Verfasst: 29. Mai 2008 14:24
von pronti
Ok, war mein Fehler. Jetzt läuft das Programm funktioniert aber nicht wie ich es wollte!
Ich kann jetzt jedes Item (mit oder ohne Children) auf ein Item ohne Children verschieben.
Was ich aber wollte war, dass ich ein Item mit Children nicht in ein anderes Item mit Children verschieben kann.
Die Children eines RootItem soll man aber in ein anderes RootItem verschieben können und die
RootItems sollen innerhalb des Widgets verschiebar sein.
Um das zu Implementieren muss ich daher wissen ob das gedragte Item Children hat.
Wie kann ich abfragen ob das gedragte QtreeWidgetItem Children hat?

Verfasst: 29. Mai 2008 14:46
von Sephral
pronti hat geschrieben: Jetzt läuft das Programm funktioniert aber nicht wie ich es wollte!
Es gibt sicher auch eine einfachere Lösung aber....

Wenn du das "Drag" auch selbst implementierst, kannst du entscheiden was überhaupt herumgezogen wird und hast natürlich auch alle Informationen über das Item.

* Beim mousePressEvent die Position speichern.
* In mouseMoveEvent (wenn links Taste noch gedrückt) die manhattanLength prüfen.
* Wenn's weit genug gezogen wurde, dann QDrag erzeugen

Verfasst: 29. Mai 2008 15:44
von pronti
Ich glaube ich habe eine Lösung wie ich das ganze machen kann.
Im Moment habe ich leider noch ein Problem mit

Code: Alles auswählen

if (item->parent() == pTreeWidget)
    // item ist ein TopLevelItem
'pTreeWidget' was not declared in this scope