[Solved] QSqlTableModel und Drag'n'Drop



  • Ich glaube grundlegend zu verstehen was du mir sagst 😃 Da ich ja aber nur

    #include <QDialog>

    includiere geh ich nun mal davon aus, das ich die Includierung anders machen muss ?



  • Dein Fehler forward delacration of 'struct QDragEnterEvent' sagt aber das du in irgendner .h so eine forward declaration gemacht hast ohne in der cpp das "Fleisch" dazu zu packen ... der weiss also das da ne Klasse QDragEnterEvent da ist, kennt aber deren .h nicht ... dir fehlt schlicht und einfach noch ein #include <QtGui/QDragEnterEvent> in der cpp. Wenn du dir die Mühe machst mein Beispiel zu compilieren ... siest du das denke ich auch.

    Um das von dir gelinkte Beispiel (auszugsweise) zu bemühen:

    http://doc.qt.nokia.com/main-snapshot/draganddrop-draggableicons-dragwidget-h.html

    #ifndef DRAGWIDGET_H
     #define DRAGWIDGET_H
    
     #include <QFrame>
    
     class QDragEnterEvent; // forward dekl.
     class QDropEvent;      // forward dekl.
    
    [snipp]
     #endif
    

    http://doc.qt.nokia.com/main-snapshot/draganddrop-draggableicons-dragwidget-cpp.html

    #include <QtGui> // <<-- wenn du das mit F2 im Creator öffnest siehst du das unten stehende 
    [snipp]
    

    QtGui-Header für Faule ... includiert so ziemlich alles aus <QtGui/*>

    #ifndef QT_QTGUI_MODULE_H
    #define QT_QTGUI_MODULE_H
    #include <QtCore/QtCore>
    #include "qbitmap.h"
    [snipp ca 220 includes unter anderem das was da mit class QIrgendwas forward deklatiert wurde ... ]
    #include "qundoview.h"
    #endif
    


  • Da alle Beispiele dich ich so gefunden haben, GtGui statt QtDialog includiert haben, hab ich das dann auch mal so gemacht, und seitdem funktioniert das kompilieren.

    Weiterhin hab ich mir anhand der 4.7er Doku und diesem Beispiel
    http://www.java2s.com/Code/Cpp/Qt/Dropaction.htm
    Quellcode zusammen gefrickelt, der durchaus funktioniert, jeder Profi würde mich dafür wahrscheinlich schlagen, aber das kannja nochbesser werden 😉

    Punkt ist, funktionieren tut die ganze Sache. Ich hab ein Listview, dem ich als Model eine QStringliste mitgebe, das ich vorher aus der Datenbank gefüttert habe, und im moment 5 Optionen anbietet. Das 2 Listview akzeptiert die Drops, die action ist auf move gestellt.

    Problem 1: Das erste Drag Item, egal welches der 5 Optionen ich wähle, wird auf der Dragseite leer, und auf der Dropseite entsteht nur ein leeres Element, jede weitere Option wird auf der Dragseite Leer und mit Exakten Namen auf der Dropseite angezeigt.

    Problem 2: Die Dragseite wird zwar "leer", aber die Elemente bleiben erhalten, ich kann also sobald ich die erste Option gedragt habe, leere Elemente draggen, die dann auch Leer auf der Dropseite hinzugefügt werden. Wie kann ich das umgehen ?



  • wenn es dich nicht stört das der 220 .h's includiert von denen du geschätzt 10 brauchst ... man kann auch mit nem 7.5t LKW 10 Brötchen beim Bäcker nebenan kaufen fahren .....................



  • padreigh schrieb:

    wenn es dich nicht stört das der 220 .h's includiert von denen du geschätzt 10 brauchst ... man kann auch mit nem 7.5t LKW 10 Brötchen beim Bäcker nebenan kaufen fahren .....................

    Ok, fehler erkannt und geändert 😃
    QtGui Raus und dafür QDialog, QDropEvent, und QDragEnterEvent rein.

    Wenn du mich jetzt noch auf den richtigen weg stößt warum das erste Drag-Event leer ist bau ich dir nen Denkmal in mein Programm 😃

    EDIT: Typos



  • Sorry, hab mit DnD noch nichts gemacht ... und bin zu müd jetzt nachzusurfen, ich schau morgen mal 🙂



  • Ich danke dir wie verrückt, ich hab dir mal meinen zusammengeschusterten Quellcode auf Pastebin gepackt.

    http://pastebin.com/VwEZDmhz

    Gute Nacht!



  • So klappts (95% copy&paste from: http://doc.trolltech.com/4.6/model-view-dnd.html) ..

    Änderung: Gegenüber QStringListModel erlaube ich nur MoveActions keine Copy Actions. Ich denke den eigenen MimeType sowie dekodierung/encodierung könnte man auch noch sparen wenn man als encodierung text/plain wählt ... war aber zu faul dazu

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QStringListModel>
    #include <QMimeData>
    
    class DragDropStringListModel : public QStringListModel
    {
        Q_OBJECT
    public:
        DragDropStringListModel ( QObject * parent = 0 )
            : QStringListModel(parent) {}
    
        DragDropStringListModel ( const QStringList & strings, QObject * parent = 0 )
            : QStringListModel(strings, parent) {}
    
        Qt::DropActions supportedDropActions() const
        {
            return Qt::MoveAction;
        }
    
        Qt::ItemFlags flags(const QModelIndex &index) const
        {
            Qt::ItemFlags defaultFlags = QStringListModel::flags(index);
    
            if (index.isValid())
                return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
            else
                return Qt::ItemIsDropEnabled | defaultFlags;
        }
    
        QStringList mimeTypes() const
        {
            QStringList types;
            types << "application/vnd.text.list";
            return types;
        }
    
        QMimeData * 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;
        }
    
        bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
        { 
    
            if (action == Qt::IgnoreAction)
                return true;
    
            if (!data->hasFormat("application/vnd.text.list"))
                return false;
    
            if (column > 0)
                return false;
    
            int beginRow;
    
            if (row != -1)
            {
                beginRow = row;
            }
            else if (parent.isValid())
            {
                beginRow = parent.row();
            }
            else
            {
                beginRow = rowCount(QModelIndex());
            }
    
            QByteArray encodedData = data->data("application/vnd.text.list");
            QDataStream stream(&encodedData, QIODevice::ReadOnly);
            QStringList newItems;
            int rows = 0;
    
            while (!stream.atEnd()) {
                QString text;
                stream >> text;
                newItems << text;
                ++rows;
            }
    
            insertRows(beginRow, rows, QModelIndex());
            foreach (QString text, newItems) {
                QModelIndex idx = index(beginRow, 0, QModelIndex());
                setData(idx, text);
                beginRow++;
            }
    
            return true;
        }
    
    };
    
    #include <QtGui/QWidget>
    
    class QTreeView;
    class QGridLayout;
    
    class DragDropWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        DragDropWidget(QWidget *parent = 0); 
    
    protected:
    
    private:
        QGridLayout * lay;
    
        QTreeView * v1;
        QTreeView * v2;
    
        DragDropStringListModel * m1;
        DragDropStringListModel * m2;
    };
    
    #endif // WIDGET_H
    
    #include "widget.h"
    
    #include <QtGui> // faul ... müde ... erkältet. lieber nur nötiges einbinden.
    
    DragDropWidget::DragDropWidget(QWidget *parent)
        : QWidget(parent)
    {
        lay = new QGridLayout(this);
    
        v1 = new QTreeView(this);
        v1->setSelectionMode(QAbstractItemView::SingleSelection);
        v1->setDragEnabled(true);
        v1->viewport()->setAcceptDrops(true);
        v1->setDropIndicatorShown(true);
    
        v2 = new QTreeView(this);
        v2->setDragEnabled(true);
        v2->viewport()->setAcceptDrops(true);
        v2->setDropIndicatorShown(true);
        v2->setSelectionMode(QAbstractItemView::SingleSelection);
    
        v1->setDragDropMode(QAbstractItemView::DragDrop);
        v2->setDragDropMode(QAbstractItemView::DragDrop);
    
        lay->addWidget(v1,0,0);
        lay->setColumnMinimumWidth(1,30);
        lay->addWidget(v2,0,2);
    
        m1 = new DragDropStringListModel(QString("1,2,3,4,5,6").split(","), this);
        m2 = new DragDropStringListModel(QStringList(), this);
    
        v1->setModel(m1);
        v2->setModel(m2);
    }
    


  • Super, funktioniert soweit mit eine paar winzigen Korrekturen die vom C&P kommen. Herzlichen Dank erstmal 😃

    Nun bleiben eigentlich nur noch kleinigkeiten (hoffe ich) 😃

    1.) Wie kann ich auf die Daten der Drop-Seite zugreifen ? Soweit ich das sehe hat ja die QStringList ja keinen Namen. Kann ich dort einfach einen festlegen ?

    2.) Kann ich die Anzahl der Drops beschränken? So das die Dropseite nur X Elemtente der Drag-Seite akzeptiert ?

    3.) Was sich auch bei deinem Beispiel nicht ändert, ist das auf der Drag-Seite nach dem Move leere Elemente entstehen die auch gedragt werden können, kann man das umgehen ?



  • wenn du mal genau schaust, HAT meine 2. View ein QStringListModel dran, das ist halt zu Anfang nur leer ... warscheinlich fehlt das bei dir und führt zu diesem Verhalten ... bei mir gibts keine leeren, drag-baren Elemente.



  • padreigh schrieb:

    wenn du mal genau schaust, HAT meine 2. View ein QStringListModel dran, das ist halt zu Anfang nur leer ... warscheinlich fehlt das bei dir und führt zu diesem Verhalten ... bei mir gibts keine leeren, drag-baren Elemente.

    Es lag daran das ich statt einem QTreeView ein QTableView hatte, nach umstellung auf QTreeView hab ich das Verhalten mit den Leeren Elementen auch nicht mehr.

    Bleiben die fragen, wie komme ich an die Daten der QStringList von m2 und kann ich die größe der QStringList von m2 Beschränken das sie nur x drops Akzeptiert?



  • Eigeninitiative ?!? API ?!?

    QStringList QStringListModel::stringList () const
    

    Schau das abgeleitete QStringListModel an, verpass dem intern ein private member int m_maxPlatz; Initialisier den im Konstruktor auf -1. Spendier dem ganzen ne public Methode void setMaxPlatz(int wieviele=-1) { if (wieviele == -1 || (wieviele > 0 && wieviele > view.strinList().size())) m_maxPlatz=wieviele; } und überprüf im dropMimeTarget(...) ob die derzeite stringlistengröße+1 größer m_maxPlatz wird, wenn ja, returniere false ... wenn m_maxPlatz == -1 dann sparste dir die Überprüfung da dann unbegrenzt platz is ...



  • Vielen Dank erstmal, ich hab nun heftig hin und her probiert. Der Weg ist soweit klar aber mit diesem Teil

    padreigh schrieb:

    ...0 && wieviele > view.strinList().size())) ...

    hab ich meine schwierigkeiten .... Mir ist nicht so ganz klar wie ich darauf zugreifen soll, ohne dabei etwas zu benutzen, was verhindern würde das ich das ganze an andere stelle einfach wiederverwenden könnte .... kannst du mir da bitte nochmal auf die Sprünge helfen ?



  • Nach ein wenig Trial&Error bin ich nun fast da angelangt wo ich hinwollte. Welche Optionen auf der Dropseite sind, diese Auszulesen etc. funktioniert. Auch das die Dropseite nur eine Anzahl X optionen akzeptiert funktioniert. Nur hab ich nun noch 1 Problem, wenn ich die Anzahl der Maximalen Plätze auf der Dropseite erreicht habe, kann ich auch nicht mehr zurückschieben.

    Mit diesem Code wird ja nachgeschaut, an welche Stelle der "Drop" eingefügt wird.

    bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
        {
    
            if (action == Qt::IgnoreAction)
                return true;
    
            if (!data->hasFormat("application/vnd.text.list"))
                return false;
    
            if (column > 0)
                return false;        
    
            int beginRow;
    
            if (row != -1)
            {
                beginRow = row;
            }
            else if (parent.isValid())
            {
                beginRow = parent.row();
            }
            else
            {
                beginRow = rowCount(QModelIndex());            
            }
    

    Kann ich irgendwie mit einem vergleich oder ähnlichem Feststellen, auf welche Stringliste da gerade geprüft wird ? Das ist mir im moment noch ein kleines Rätsel ....



  • Dann hast du warscheinlich was anders gemacht als ich vorgeschlagen hab ... zB die Anzahl hardgecoded? Sonst wäre es einfach in der einen QStringListModelKlasse lässt du m_maxPlatz auf -1 (== unbegrenzt), in der anderen wo es beschränkt ist, beschränkst du es halt ...

    ich meinte sowas:

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QStringListModel>
    #include <QMimeData>
    
    /* alle unrelevanten Stellen entFERNT */
    
    class DragDropStringListModel : public QStringListModel
    {
        Q_OBJECT
    private:
        int m_maxItems; /* HIIIIIIIIIIIIIER */
    
    public:
        DragDropStringListModel ( QObject * parent = 0 )
            : QStringListModel(parent), m_maxItems(-1) {} /* HIIIIIIIIIIIIIER */
    
        DragDropStringListModel ( const QStringList & strings, QObject * parent = 0 )
            : QStringListModel(strings, parent), m_maxItems(-1) {} /* HIIIIIIIIIIIIIER */
    
        void setMaxItems(int nr = -1) { if (nr > 0 && nr < stringList().size()) m_maxItems = nr; else m_maxItems=-1; } /* HIIIIIIIIIIIIIER */
    
        bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
        {
    
            if (action == Qt::IgnoreAction)
                return true;
    
            if (!data->hasFormat("application/vnd.text.list"))
                return false;
    
            if (column > 0)
                return false;
    
            if (m_maxItems == stringList().size()) /* HIIIIIIIIIIIIIER */
                return false;
    
            // usw.
        }
    };
    

    sollte tun ...



  • Naja, etwas. Also erstmal hab ich ne Methode zum setzen der m_maxPlatz geschrieben. Dann prüf ich in der

    Qt::DropActions supportedDropActions() const
    

    Ob Maxplatz -1 ist, oder ne Hilfsvariable z < Maxplatz. Wenn das gegeben ist, dann ActionMove, wenn nicht ActionIgnore.
    Nur wenn ActionMove ist, wird ja dann die

    bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
    

    durchlaufen, dort erhöhe ich die hilfsvariable z um 1.

    Und genau da ist das Problem ... denn sowohl von Rechts nach Links, als auch von Links nach rechts, wird ja z um 1 größer. Helfen würde mir nun, wenn ich die Richtung unterscheiden könnte, und danach z erhöhe oder verringere. Aber hier komm ich nicht weiter ....
    Nen wirklich anderen Lösungsansatz hab ich irgendwie nicht gefunden, weil ich ja kein Objekt auf meine Vas-Klasse erstellen will, womit ich die größe der Stringliste in m1 bzw m2. auslesen könnte. Denn ich würde gern die hierfür erstelle model.h gern an deren Stelle im Programm wieder verwenden.



  • Hilfsvariable z HÄH? ... schau dir meinen CodePost wo /* HIIIIIIIIIIIIIER */ steht ...

    Bei der Instanziierung

    DragDropStringListModel m1;
    DragDropStringListModel m2;
    QTreeView v1;
    QTreeView v2;
    
    v1.setModel(m1);
    v2.setModel(m2);
    
    m2.setMaxItems(10);
    

    Feddich ist ... dann prüft das eine model beim dropppen nicht ( da maxAnzahl auf -1) das andere prüft (da maxAnahl>-1). Das DragDropStringListModel erbt von QStringListModel und wird doch wohl noch seine eigene stringList().size() abfragen dürfen!?! Ich hoffe du trennst das auch wieder in .cpp und .h auf ... ich poste das hier nur so reinimplementiert weil das fürs Forum kürzer ist ... in die .h nur die Bekanntmachung der Methoden, in die .cpp die Implementierung !!



  • nalamar schrieb:

    bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
    

    Wenn ich das richtig deute sagt dir action was passieren soll ... ist das nicht möglich returnierst du false, sonst machst du es und returnierst true ... du kannst natürlich die action beliebig umsetzen, das hat aber keine Konsequenz da die als Kopie hier reinkommt und all deine Änderungen am Ende der Methode null und void sind ...



  • Sorry, ich hab dein Edit-Code-Beispiel erst jetzt gesehen, da hatte ich schon geantwortet. Ja, zu 90% sah mein Code auch so aus.
    Nur das hatte ich nicht.

    if (m_maxItems == size()) /* HIIIIIIIIIIIIIER */ 
                return false;
    

    Dort hatte ich eben ein z++; und hab eben in den supportedDropActions geschaut ob z > m_maxPlatz wird.

    Und das size() meint der Kompiler ist nicht deklariert in dem Scope. Mal ganz abgesehen davon das ich so ziemlich jede Variante durchversucht haben mit parent und QModelIndex an irgend ne verwertbare Größe zu kommen und was weiß ich nicht alles, ich komme eben nun mal nicht an die Size der Stringliste .... aber ich tu mein bestes das noch rauszufinden .....

    EDIT sagt: Ok ich sehe du hast dein Posting nochmal abgeändert, dann vergiss erstmal was ich geschrieben habe ... ich setze erstmal um was ich nun daraus an erkenntniss gewonnenhabe, sieht grad gut aus 😃



  • Sooo .... nach 3 Bissen in die Tischkante funktioniert nun alles ....

    Erstmal hatte ich das Problem das m_maxPlatz irgendwie nicht größer als -1 wurde .... bis mir dann auffiel das ich aus deinem Codebeispiel von Seite 1 noch folgendes hatte

    m1 = new DragDropStringListModel(QString("1,2,3,4,5,6").split(","), this); 
        m2 = new DragDropStringListModel(QStringList(), this);
    

    Nach 5 mal durchlesen und verzweifeln das abgeändert .... und fast am Ziel gewesen. Dann blieb folgendess Problem - wenn ich die Überprüfung auf size>m_maxPlatz wirklich in die

    bool dropMimeData(const QMimeData *data,
                          Qt::DropAction action,
                          int row, int column, const QModelIndex &parent)
    

    Funktion packe, und die size+1>maxPlatz wird, ist es egal ob du false oder true returnierst, das DragItem verschwindet im Nichts. Also Back zu meiner Ursprünglichen Idee und das ganze nun so aufgebaut.

    Qt::DropActions supportedDropActions() const
        {
            if (m_maxPlatz == -1 || stringList().size()+1 <= m_maxPlatz)
            {
                return Qt::MoveAction;
            }
            else
            {
                return Qt::IgnoreAction;
            }
        }
    

    Und es funktioniert genau so wie ich mir das gewünscht habe. Padreigh, ich danke für deine Engelsgeduld.

    Ich wünsche dir eine Gute Nacht ! 😃

    EDIT: Mist, ich korrigiere meine Aussage. Wenn MaxPlatz erreicht wird funktioniert auch das zurückdraggen nicht mehr *grübel*

    EDIT2: Ok, sobald das Draggen beginnt wird das erstmal mal supportedActions aufgerufen, da das Item dabei aber noch auf dem Focus der Liste ist, die Beschränkt ist, kommt als supportedaction "IgnoreAction" und das zurückdraggen funktioniert nicht. Also muss ich eine 3. Stelle finden, wo die Abfrage auf Beschränkung sinn macht.


Anmelden zum Antworten