PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Drag and Drop



Kirsche
22-08-2005, 23:36
Hallo Leute,

ich möchte per Drag and Drop ein Fenster öffnen und bin nicht sicher, wie ich es realisieren soll. Ich habe mir verschiedene Beispiele angesehen, aber nichts Passendes gefunden.

Was ich machen möchte:
Ich habe auf der linken Seite des Programmes einen Baum (QListView) mit Dateinamen. Wenn ich einen Doppelklick auf ein Item mache, wird rechts ein Fenster geöffnet, die entsprechende Datei eingelesen und Daten im Fenster gesetzt. Nun möchte ich denselben Effekt per Drag and Drop erzeugen. Ich möchte also auf ein Item klicken, es festhalten, nach rechts ziehen und dort loslassen. Dann soll sich das entsprechende Fenster öffnen. Kann man das irgendwie machen?

Ich habe es schon mit QDropEvent etc. versucht, aber ich weiß gar nicht, wie die Events überhaupt ausgelöst werden. Ich benutze item->setDragEnabled( true );" , aber es passiert nichts.
Muss ich eine eigene Klasse von QListView ableiten, um QDragObject * QListView::dragObject () [virtual protected] selbst zu schreiben? Kann ich darin einfach eine Funktion aufrufen?

Vielen Dank,
Kirstin

anda_skoa
23-08-2005, 09:52
ich möchte per Drag and Drop ein Fenster öffnen und bin nicht sicher, wie ich es realisieren soll. Ich habe mir verschiedene Beispiele angesehen, aber nichts Passendes gefunden.

Ein extra Fenster öffnen oder in einem vorhandenen Fenster etwas neues anzeigen?



Muss ich eine eigene Klasse von QListView ableiten, um QDragObject * QListView::dragObject () [virtual protected] selbst zu schreiben?

Ja, am besten eine Instanz von QUriDrag erzeugen da du ja Dateinamen hast.



Kann ich darin einfach eine Funktion aufrufen?

Das macht QListView selbst.

Das DropEvent ist erst im Empfänger relevant, d.h. der bekommt zuerst ein DragEnter Event, dann DragMoveEvents und zum Schluß eben potentiell ein DropEvent.

Ciao,
_

Kirsche
23-08-2005, 11:27
Hallo anda_skoa,

danke für deine Antwort.

Ich möchte beides: wenn noch kein Fenster geöffnet ist, möchte ich ein Fenster öffnen, wenn schon eins geöffnet ist, Daten hinzufügen. Geht das?

Sieht man das Ereignis eigentlich, wird also irgendwas auch optisch verschoben?

Schöne Grüße,

Sid
23-08-2005, 13:06
Hallo Kirsche,
ich bin zwar selber noch blutiger Qt-Anfänger, aber das mit dem QListView DnD hab ich Einigermaßen kapiert.
Also, du erstellst per dragObject() ein QUriDrag, etwa so:

QDragObject * DragListView::dragObject()
{
QStringList Filenames;
for( QListViewItemIterator ititems( this->firstChild()) ; ititems.current() ; ++ititems)
{
if ((*(*ititems)).isSelected())
{
Filenames += (*(*ititems)).text(0);
}
}
QUriDrag* d = new QUriDrag(this);
d->setFileNames(Filenames);
return d;
}
(Das ist von mir, wenn mehrere Filenames verwendet werden. Musst halt ein wenig anpassen).
Das ganze verändert dann auch den Cursor, und du kannst sogar noch ein Pixmap angeben, das dann "mitgezogen" wird.
Wenn du dann damit über ein anderes Widget kommst, bekommt dieses eben die Signale, wie Anda_Skoa gesagt hat, und mit deiner eigenen Klasse sagst du dann, was passiert, wenn es wirklich dieses Drop event gibt. Aber da muss ich einfach auf das Qt buch (das ebook von blanchette usw. gibts auf der trolltech seite iirc) verweisen, da ist das super erklärt, nur der Teil mit den QListViews ist da leider nicht drin.

Naja, viel Glück und Spaß (was tolles: man kann auch aus z.B. Konqueror dann in dein Programm ziehen, das erzeugt auch diese QUriDrags ;))

Kirsche
23-08-2005, 13:41
Hallo Sid,

danke für die Erklärungen. Ich habe sogar das Buch von Jasmin Blanchette, aber wie du gesagt hast, fehlt QListView. Ich wusste nicht, wie ich die Erklärungen dafür umsetzen sollte.
Wenn ich deinen Code richtig verstehe, leitest du QListView ab (genannt DragListView) und erstellst darin eine Funktion dragObject(), oder?
Wenn ich dann noch ein Bild angebe, sehe ich, ob sich was tut oder nicht. Daran kann ich erkennen, ob zumindest Drag funktioniert, oder? Wenn ich dann loslasse passiert vermutlich erst einmal noch nichts.
"Drop" muss ich dann für mein QWorkspace implementieren, richtig? Geht das so, oder muss ich davon auch eine Klasse ableiten? Muss ich einen Eventfilter benutzen?
Ich denke, beim DropEvent muss ich dann nur noch unterscheiden, ob schon ein Fenster geöffnet ist oder nicht. Angenommen, ich lasse mein Objekt schon los, bevor ich das geöffnete Fenster errreiche: Soll ich es dann trotzdem öffnen, oder sollte man schon genau treffen? Dann müsste ich nämlich zwei Sachen kontrollieren, nämlich ob ich den Workspace treffe (wenn das Fenster zu ist) bzw. ob ich das geöffnete Fenster treffe. Was ist sinnvoll?

Schöne Grüße,

anda_skoa
23-08-2005, 16:16
Wenn ich deinen Code richtig verstehe, leitest du QListView ab (genannt DragListView) und erstellst darin eine Funktion dragObject(), oder?

Korrekt



Wenn ich dann noch ein Bild angebe, sehe ich, ob sich was tut oder nicht.

Das Bild ist optional. man hat auch ohne Bild eine Drag Visualisierung.



Daran kann ich erkennen, ob zumindest Drag funktioniert, oder? Wenn ich dann loslasse passiert vermutlich erst einmal noch nichts.

Korrekt



"Drop" muss ich dann für mein QWorkspace implementieren, richtig? Geht das so, oder muss ich davon auch eine Klasse ableiten? Muss ich einen Eventfilter benutzen?

Sollte beides gehen.
Ableiten ist vermutlich sauberer.



Soll ich es dann trotzdem öffnen, oder sollte man schon genau treffen?

Wenn es nicht auf Grund von Vorgaben unmöglich sein soll zwei Dateien zu öffnen, würde ich ein bereits offenes Fenster nur ändern, wenn es genau getroffen wird.

Ciao,
_

Kirsche
23-08-2005, 22:56
Hallo Sid, hallo anda_skoa,

"Drag" funktioniert jetzt. Nur "Drop" geht noch nicht. Wenn ich den QListViewItem nach rechts ziehe, erscheint ein durchgestrichener Kreis, der vermutlich bedeutet, dass dort ein "Drop" nicht akzeptiert wird. Woran kannn das liegen? Hier ist mein Code ( ich möchte einfach eine Message ausgeben, um zu gucken, ob es klappt):

...
mdi = new QWorkspace( this, "workspace" );
mdi->setAcceptDrops( true );
setCentralWidget( mdi );
...

bool MainWindow::eventFilter( QObject *obj, QEvent *e )
{
if ( obj == mdi )
{
if ( e->type() == QEvent::Drop)
{
QMessageBox::information ( this, "Information", "DropEvent", QMessageBox::Ok , 0, 0 );

return false;
}
else
{
return false;
}
}
else
{
// pass the event on to the parent class
return QWidget::eventFilter( obj, e );
}
}

Vielen Dank,

anda_skoa
24-08-2005, 10:09
Du mußt das Event accepten, weil sonst das D&D nicht weiß, daß du die Daten wirklich transferiert haben willst.

Ciao,
_

Kirsche
24-08-2005, 10:36
Hallo anda_skoa,

irgendwie klappt das trotzddem nicht. Meintest du das so mit dem "accept" ?

...
if ( e->type() == QEvent::Drop)
{
QDropEvent* d = (QDropEvent*)e;
d->accept( true );
const char *str = d->format( 0 );
QString fileName( str );

QMessageBox::information ( this, "Information", fileName, QMessageBox::Ok , 0, 0 );

return false;
}
...

Ich möchte einfach nur den ersten Dateinamen ausgeben (passiert aber nicht) Später kommt stattdessen ein Funktionsaufruf.

Schöne Grüße,

anda_skoa
24-08-2005, 12:18
QMimeSource::format gibt das Format der Drag&Drop Daten an, also in diesem Fall application/x-uri-list oder so ähnlich.

Wenn man nur einen Typ erwartet macht man das normalerweise so



event->accept(QUriDrag::canDecode(event));

D.h man fragt seine Drag Klasse, ob sie mit den Daten was anfangen kann.

Vermutlich mußt du aber auch DragEnter und DragMove Event behandeln

Ciao,
_

Sid
24-08-2005, 13:06
Ich hab in meiner Klasse (leider recht groß geworden, da ich dieses zwischen den items einsetzten hab wollte aber keine kde dependencies ;)) das accept im ContentsDragMoveEvent.

Kirsche
24-08-2005, 17:53
Hallo anda_skoa, hallo Sid,

ich habe beides ausprobiert, aber irgendwie geht es immer noch nicht.

1.
QDropEvent* d = (QDropEvent*)event;
d->accept(QUriDrag::canDecode(d));

Dabei passiert nichts. Woher soll mein Workspace wissen, ob es die Daten akzeptieren darf oder nicht? Ich möchte beim "Drop" schließlich eine Funktion aufrufen.

2.
QEvent::DragMove funktioniert zwar, aber dann tritt das Ereignis ein, sobald ich über den Workspace fahre. Oder soll ich dort nur dafür sorgen, dass sich der Mauszeiger in ein anderes Symbol verwandelt?

Vielen Dank,

Sid
24-08-2005, 18:22
Zieh doch mal testweise dein QUriDrag dings innen konqueror. der sollte das akzeptieren.

QDropEvent* d = (QDropEvent*)event;
d->accept(QUriDrag::canDecode(d));

Da kannst du dir eigentlich die erste Zeile sparen.


2.
QEvent::DragMove funktioniert zwar, aber dann tritt das Ereignis ein, sobald ich über den Workspace fahre. Oder soll ich dort nur dafür sorgen, dass sich der Mauszeiger in ein anderes Symbol verwandelt?

Du sollst bei DragMove das d->accept() machen. Dann weiß qt, dass es da hin droppen kann und der mauszeiger ändert sich automatisch. kannst ja mal testweise alles accepten.
Später kannst du dann beim Dropevent das machen, was passieren soll.

Kirsche
24-08-2005, 22:35
Hallo Sid, hallo anda_skoa

vielen Dank für eure Hilfe. Es klappt jetzt! So habe ich es gemacht:

if ( event->type() == QEvent::DragMove )
{
QDragMoveEvent* d = (QDragMoveEvent*)event;
d->accept( true );

return false;
}

und

if ( event->type() == QEvent::Drop)
{
QDropEvent* d = (QDropEvent*)event;
d->accept( true );

// get name(s) of file(s)
QStringList list;
QUriDrag::decodeLocalFiles ( d, list );
QString fileName;

for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
{
QFileInfo file( *it );
fileName = file.fileName();
// Funktionsaufrufe
}
}

Schöne Grüße,

anda_skoa
25-08-2005, 09:23
Ich würde schon dazu raten, QUriDrag::canDecode zu benutzen um festzustellen, ob du das Event akzeptieren willst.

Derzeit akzeptierst du jede Art von Drag und auch den Drop und würdest bei etwas anderem als einem UriDrag dann beim Dekodieren in eine Fehler Situation geraten.

Besser gleich jeden Nicht-UriDrag ablehnen.

Ciao,
_

Kirsche
25-08-2005, 10:26
Hallo anda_skoa,

woher weiß mein Workspace, dass er UriDrags akzeptieren darf? Das muss ich ihm doch extra sagen, oder? Wie mache ich das? Im MoveEvent?

Schöne Grüße,
Kirstin

anda_skoa
25-08-2005, 13:19
Gleicher Code wie bisher, nur eben kein hartkodiertes "true" für die accept()

Ciao,
_

Kirsche
26-08-2005, 08:37
Hallo anda_skoa,

danke für den Hinweis. Ich habe es jetzt geändert. :)

Schöne Grüße,