[Solved] QSqlTableModel und Drag'n'Drop



  • Hallo, da bin ich mal wieder ....

    dank eure Hilfe schreitet mein Programm munter fort, und nun bin ich auf das nächste Problem gestoßen. Ich würde gern daten von einem TableModel in ein Listview per DnD hin und herschieben.

    Also hab ich mir die Docu für qt 4.7 genommen und mir die DnD Beispiele angesehen, dachte mir "bringst erstmal das Original in deine Anwendung, und wandelst es dann ab wie du es brauchst" .... also Copy&Paste ... und schauen was passiert.

    Und nun erhalte ich 57 Fehler die meist folgendes umfassen:

    forward delacration of 'struct QDragEnterEvent'

    o.ä.

    Wie gesagt, die inspirationsquelle war folgendes : http://doc.qt.nokia.com/main-snapshot/draganddrop-draggableicons.html

    Hat denn jemand die Geduld mir diesmal zu helfen ? 🙄

    der Nala



  • /* deine.h */
    #include <QtGui/QWidget>
    
    class QVBoxLayout; // <---- vorwärtsdeklaration, 
    
    class MyWid : public QWidget {
    Q_OBJECT
    public:
      MyWid(QWidget * parent = 0);
    private:
      QVBoxLayout * layout;
    };
    
    /* deine.cpp */
    
    #include "deine.h"
    
    // #include <QtGui/QVBoxLayout>  // kommentiers ein und aus, und kompiliers ... vergleiche Fehlermeldungen
    
    MyWid::MyWid(QWidget * parent) : QWidget (parent) {
      layout = new QVBoxLayout();
    }
    


  • 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 ...


Anmelden zum Antworten