[gelöst] Drag'n'Drop von List-Model/View zu QTextEidt (MIME

Alles rund um die Programmierung mit Qt
Antworten
CBM
Beiträge: 38
Registriert: 6. Mai 2009 19:09

[gelöst] Drag'n'Drop von List-Model/View zu QTextEidt (MIME

Beitrag von CBM »

Hallo,

Ausgangslage und Zielsetzung
Ich habe ein Model/View, welches mir eine Liste an Strings anzeigt. Das Model ist abgeleitet von QAbstractListModel und beim View handelt es sich um QListView. Von diesem Model/View möchte ich eine Zeile per Drag'n'Drop in ein abgeleitetes QTextEdit Feld kopieren.

Das Problem
Beim Loslassen über dem QTextEdit-Feld wird nicht der gewünschte Text sondern irgendwelche Sonderzeichen kopiert.

Fehlersuche
Ich habe mich solange an der Doku http://doc.qt.nokia.com/4.4/model-view-dnd.html orientiert bis es zum Thema der MIME-Data kam. Hier habe ich leider mehr blind Quellcode abgeschrieben, anstatt diesen wirklich zu verstehen. Jedoch gibt es zum Stichwort MIME in diesem Forum eigentlich keine Beiträge und auch hilft mir ausnahmsweise weder Google noch die Qt Doku richtig weiter. Somit nun hier meine Verständnisfragen.

Verständnisfragen
In der Doku wird für den Export aus dem Model/View ein eigener MIME-Type "application/vnd.text.list" definiert:

Code: Alles auswählen

 QStringList DragDropListModel::mimeTypes() const
 {
     QStringList types;
     types << "application/vnd.text.list";
     return types;
 }
und erzeugt den encoded Inhalt durch:

Code: Alles auswählen

 QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
 {
     QMimeData *mimeData = new QMimeData();
     QByteArray encodedData;

     QDataStream stream(&encodedData, QIODevice::WriteOnly);

     foreach (QModelIndex index, indexes) {
         if (index.isValid()) {
             QString text = data(index, Qt::DisplayRole).toString();
             stream << text;
         }
     }

     mimeData->setData("application/vnd.text.list", encodedData);
     return mimeData;
 }
Erste Frage: Warum ein eigener MIME-Type? Könnte man denn nicht einfach mit "plain/text" arbeiten. Man will ja schließlich nur den Text vom Item und könnte sich somit doch später beim Drop ein separat zu implementierendes Decoding ersparen.

Zweite Frage (hoffentlich ist diese Frage nicht allzu bescheuert): Wer ruft die Funktion "*DragDropListModel::mimeData" eigentlich auf, denn wohin übergibt denn "return mimeData;" den encoded Inhalt?

Irgendwie klar, wenn man was encoded, dann muss man es später auch decoden. Das Beispiel in der Doku gibt hierfür folgendes Beispiel

Code: Alles auswählen

QByteArray encodedData = data->data("application/vnd.text.list");
     QDataStream stream(&encodedData, QIODevice::ReadOnly);
....
Dritte Frage: Da ich leider nicht verstanden habe, wie man nun außerhalb der Klasse DragDropListModel auf den encoded Inhalt zugreifen kann (siehe zweite Frage), ist mir nun auch unklar, wo im Beispiel die Klasse data herkommt (QByteArray encodedData = data->data("application/vnd.text.list");

Ich habe das Ganze, so wie ich dachte das es eigentlich auch klappen müsste als kompilierbare Minimalversion in Form einer zip-Datei angehängt. Kurzbeschreibung zur Anlage:
Es gibt zwei Klassen. Eine für das Model mit den QStringList Daten. Aus dieser Klasse sollen Textzeile per Drag'n'Drop heraus kopiert werden:

Code: Alles auswählen

class subclassed_Model : public QAbstractListModel {
    Q_OBJECT
	public:
    subclassed_Model(const QStringList &strings, QObject *parent = 0);
		// Item data handling
		int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
		QVariant data( const QModelIndex & index, int role ) const;

		// Drag-n-Drop support and MIME data handling
		Qt::DropActions supportedDragActions() const;
		Qt::ItemFlags flags(const QModelIndex &index) const;
		QMimeData *mimeData(const QModelIndexList &indexes) const;
	private:
		QStringList stringListe;
};
Der Export/encoding:

Code: Alles auswählen

QMimeData *subclassed_Model::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    foreach (QModelIndex index, indexes) {
        if (index.isValid()) {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }
    mimeData->setData("text/plain", encodedData);
    return mimeData;
}
Sowie ein Klasse abgeleitet von QTextEdit, in dem der Drop statt finden soll:

Code: Alles auswählen

class subclassed_QTextEdit : public QTextEdit {
	Q_OBJECT
	public:
		subclassed_QTextEdit(QWidget *parent=0);
	protected:
		void dragEnterEvent(QDragEnterEvent *event);
		void dragMoveEvent(QDragMoveEvent *event);
		void dropEvent(QDropEvent *event);
};
Bei dem eigentlich ein Decoding fehlt (siehe meine Verständnisfragen). D.h. beim Drop-Event müsste doch was geändert werden (doch was???):

Code: Alles auswählen

void subclassed_QTextEdit::dropEvent(QDropEvent *event) {
	const QMimeData *mimeData = event->mimeData();
	    	 append(mimeData->text());

	event->setDropAction(Qt::CopyAction);
	event->accept();
}
Entschuldigt die Länge dieses Beitrages. Aber ich hoffe, dass durch diese etwas ausführliche Beschreibung spätere Leser das Problem und durch die hoffentlich folgenden klärenden Kommentare besser ein Drag'n'Drop mit Export/Import bzw. En- und Decoding nachvollziehen können. Ist ja auch anscheinend einer der ersten Beiträge zu diesem Thema.
Dateianhänge
drop_and_mime.zip
Alle Dateien zum kompilieren (main.cpp, drop_and_mime.cpp, drop_and_mime.h, Drop_and_Mime.pro).
(2.35 KiB) 198-mal heruntergeladen
Zuletzt geändert von CBM am 8. August 2011 19:46, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Drag'n'Drop von List-Model/View zu QTextEidt (MIME Probl

Beitrag von Christian81 »

Bei QMimeData *subclassed_Model::mimeData() kodierst Du Deine Daten schön in einen QDatastream aber beim späteren auslesen nimmst Du einfach mimeData->text() - wie soll das gehen?
Der Rest der Fragen - Qt ruft diese Funktionen auf und schickt sie dann an das OS bzw. eben umgekehrt. Qt kümmert sich also um die Datenübertragung. Du musst die Daten nur korrekt verpacken (was Du ja machst) und korrekt entpacken (was Du nicht machst).
Da Du die Daten nicht als plain text schickst sondern mit einem QDataStream verpackst ist plain/text nicht der korrekte Mime-Type. Also brauchst Du einen eigenen da ja nur Du definierst wie die Daten verpackt werden.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
CBM
Beiträge: 38
Registriert: 6. Mai 2009 19:09

Re: Drag'n'Drop von List-Model/View zu QTextEidt (MIME Probl

Beitrag von CBM »

ok. Damit ist meine Vermutung schon mal bestätigt, dass ich NICHT korrekt entpacke. Für diese Bestätigung bin ich schon mal dankbar, da ich jetzt klar meine Problemlösungsversuche eingrenzen kann. Jetzt mach ich aber mal zuerst eine Pause um den Kopf wieder frei zu kriegen, und überlege mir dann wie man VOM OS die Daten wieder zurückbekommt und richtig entpackt. Melde mich dann wieder (hoffentlich mit eine funktionierenden Lösung).
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Drag'n'Drop von List-Model/View zu QTextEidt (MIME Probl

Beitrag von Christian81 »

dropEvent() ist da schon die richtige Funktion. Dort kommst Du ja auch auf QMimeData wo Du schauen solltest ob der Mime-Type übereinstimmt (bzw. ob er dabei ist, QMimeData hat ja eine Liste von mimetypes wenn ich richtig liege) und wenn ja -> Daten per QMimeData::data() holen und dekodieren.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Drag'n'Drop von List-Model/View zu QTextEidt (MIME Probl

Beitrag von franzf »

Dekodieren z.B. wieder über einen QDataStream. Kleines Testprogramm (das du so auch machen hättest können):

Code: Alles auswählen

#include <QByteArray>
#include <QString>
#include <QDataStream>
#include <QDebug>
#include <QMimeData>

int main() {
    QString s("hallo");
    QByteArray ba;
    QDataStream writer(&ba, QIODevice::WriteOnly);
    writer << s;
    
    QMimeData md;
    md.setData("text/plain", ba);
    qDebug() << md.text();
    
    QDataStream reader(ba);
    QString s2;
    reader >> s2;
    qDebug() << s2;
}
mimeData() bekommt aber eine Indexliste. Um jetzt die Listen-Daten von einem einfachen plaintext unterscheiden zu können (denk einfach an einen Drop von ListView zu ListView, da willst du auch viele Items in viele Items droppen können, da wäre ein simpler Plaintext nicht zielführend, da er nur einen Eintrag enthält), kann man diesen zusätzlichen text.list-Mimetype einführen.
Nun kannst du statt dem direkten Streamen in einen QDataStream (in mimeData()) erstmal eine QStringList erstellen und die dann in den DataStream hauen. Nachher das ByteArray wie oben in eine QStringList streamen und die Items einzeln im TextEdit anhängen.
CBM
Beiträge: 38
Registriert: 6. Mai 2009 19:09

Re: Drag'n'Drop von List-Model/View zu QTextEidt (MIME Probl

Beitrag von CBM »

Zunächst einmal besten Dank für die schnellen Antworten.
Ich habe das Problem jetzt auch gelöst. Die Lösung brachte folgende Zeile in der dropEvent-Funktion:

Code: Alles auswählen

    QByteArray encodedData = event->mimeData()->data("application/vnd.text.list"); 
Dadurch sieht die Funktion nun so aus:

Code: Alles auswählen

void subclassed_QTextEdit::dropEvent(QDropEvent *event) {
    QByteArray encodedData = event->mimeData()->data("application/vnd.text.list");


    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        append(text);
    }
    return;
}
Ich muss gestehen selbst noch nicht exakt verstanden zu haben, wie das tatsächliche Zusammenspiel von QMimeData und den Drag'n'Drop-Funktionen ist. Aber zumindest kann ich diesen Thread jetzt mal als gelöst markieren .... und werde bestimmt nochmals mit der einen oder anderen Verständnisfrage diesbezüglich hier auftrauchen. Doch zuerst will ich jetzt mal mit der funktionierenden Lösung rumspielen ... sollte auch helfen mein Verständnis zu verbessern.
P.S.: das Ganze nun funktionierende Beispiel habe ich nochmals komplett als zip-Datei angehängt.
Dateianhänge
drop_and_mime.zip
(2.61 KiB) 196-mal heruntergeladen
Antworten