Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QStringListModel Subclass Drag and Drop
Forum Updated to NodeBB v4.3 + New Features

QStringListModel Subclass Drag and Drop

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 3 Posters 611 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kelenyche
    wrote on last edited by
    #1

    Good Morning!

    I'm working on a portion of my application that uses multiple QListViews to manage some data stored in .json files. Basically the data is stored hierarchically, with three layers. One QListView is used to show available components for each layer at a time (say layer 1, 2, or 3), and the other three are used to show the contents under an individual hierarchy, for example when a single level 1 hierarchy is selected. This part isn't as important, just some background.

    So I subclassed QStringListModel to add some json parsing functionality to it. I have rowCount() and data() both implemented, and they're working great, everything I need from the jsons is filling the views perfectly, and when I select a single item from a view I'm able to show some other information I need from the json. The next step is enabling drag+drop between the views, which I'm struggling with.

    I've made a test program that uses drag+drop just fine, using just QStringListModel. Since I'm subclassing that class, I assumed setting it up the same way would enable dragging+dropping just fine. What I'm seeing is that I can drag and drop, but when I drop, all that is appended to the QStringList is a null string.

    I feel like I'm maybe missing a virtual function that needs to be overridden in my subclass? I don't understand what function gets called when you drop something into a QListView to handle the transfer of the data over - I need access to that so I can populate some other stuff based on it from the json files.

    I didn't post any code since I feel like this is a broader question about subclassing properly - feel free to ask for parts of it if you think it'll help!

    Thanks!

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Your code is indeed needed. Otherwise it is just guesswork. If an empty string is inserted you might have an issue the way your create the data to send or in the way you manage your drop.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • K Offline
        K Offline
        Kelenyche
        wrote on last edited by
        #3

        Yea fair enough. I guess I was looking for some broader guidance on how to subclass a model, but you're right. I'm new to the Model/View system, so I'm definitely still wrapping my brain around it.

        So I'm subclassing QStringListModel like so:
        jsonListModel.h:

        #include <QStringListModel>
        #include "json/single_include/nlohmann/json.hpp"
        
        using json = nlohmann::json;
        
        class JsonListModel : public QStringListModel
        {
            Q_OBJECT
        
        public:
            explicit JsonListModel(std::string newJsonIdentifier, QObject *parent = nullptr);
        
            std::string jsonIdentifier;
            std::string selectedCycle;
            std::string selectedStage;
            std::string selectedSegment;
            int modeFlag = 1; //hard code this to only read from the segments.json
            int rows = 0; //hard code to zero in case we look too early
            void updateJsonStringList();
            json readFromJson(std::string identifier);
            //std::vector<std::string> jsonStringList;
            QStringList jsonStringList;
            json newJson;
        
            int rowCount(const QModelIndex &parent = QModelIndex()) const override;
            QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
            bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) const;
            QStringList getJsonSubdata(std::string jsonIndex);
        };
        

        I pared this down a bunch to remove unrelated stuff, but here's the source, jsonListModel.cpp:

        #include <QDebug>
        #include <iostream>
        #include <fstream>
        #include <QStringList>
        #include <experimental/filesystem>
        
        JsonListModel::JsonListModel(std::string newJsonIdentifier, QObject *parent) : QStringListModel(parent)
        {
            jsonIdentifier = newJsonIdentifier;
            updateJsonStringList();
            //this populates the stringlist as soon as the constructor is called
        }
        
        int JsonListModel::rowCount(const QModelIndex & /*parent*/) const
        {
            return rows;
        }
        
        QVariant JsonListModel::data(const QModelIndex &index, int role) const 
        {
            int row = index.row();
            if(role == Qt::DisplayRole)
            {
                return jsonStringList.at(row);
            }
            return QVariant();
        }
        
        void JsonListModel::updateJsonStringList()
        {   
           //A bunch of stuff happens here to fill up jsonStringList with data from the json files
            setStringList(jsonStringList);
        }
        

        Then in the header of the widget that holds the jsonStringListModels:

        //QListViews
            QListView* jsonList;
            QListView* cycleList;
            QListView* stageList;
            QListView* segmentList;
        
            //SegmentTableModels
            JsonListModel* jsonCycleListModel;
            JsonListModel* jsonStageListModel;
            JsonListModel* jsonSegmentListModel;
        
            JsonListModel* cycleListModel;
            JsonListModel* stageListModel;
            JsonListModel* segmentListModel;
        

        And the source of that widget:

        //Models
            jsonCycleListModel = new JsonListModel("jsonCycles");
            jsonStageListModel = new JsonListModel("jsonStages");
            jsonSegmentListModel = new JsonListModel("jsonSegments");
        
            cycleListModel = new JsonListModel("cycles");
            stageListModel = new JsonListModel("stages");
            segmentListModel = new JsonListModel("segments");
        
        //QListViews
            jsonList = new QListView(this);
            jsonList->setDragEnabled(true);
            jsonList->setAcceptDrops(true);
            jsonList->setDropIndicatorShown(true);
            jsonList->setModel(jsonCycleListModel);
            jsonList->setStyleSheet(
                "QListView { font-size: 10pt; font-weight: bold; }"
                "QListView::item { background-color: #E74C3C; padding: 10%;"
                "border: 1px solid #C0392B; }"
                "QListView::item::hover { background-color: #C0392B }");
        

        I have four QListViews in here, but they're all basically the same, so I don't see the need in posting all of them.

        So now that I've been looking at this a little closer, I suspect the issue is with my call of setStringList(jsonStringList); - it feels like I should be appending to the model's stringList? I guess I'm not clear if that call attaches my stringlist to the model, or if it just copies the contents over. The other possible issue is what I mentioned in the first post, that I'm missing an override of a virtual function somewhere. I've been doing a bunch of document reading, and there's a bunch of stuff about MIME data, but my understanding was that that would be taken care of by the QStringListModel already.

        VRoninV 1 Reply Last reply
        0
        • K Kelenyche

          Yea fair enough. I guess I was looking for some broader guidance on how to subclass a model, but you're right. I'm new to the Model/View system, so I'm definitely still wrapping my brain around it.

          So I'm subclassing QStringListModel like so:
          jsonListModel.h:

          #include <QStringListModel>
          #include "json/single_include/nlohmann/json.hpp"
          
          using json = nlohmann::json;
          
          class JsonListModel : public QStringListModel
          {
              Q_OBJECT
          
          public:
              explicit JsonListModel(std::string newJsonIdentifier, QObject *parent = nullptr);
          
              std::string jsonIdentifier;
              std::string selectedCycle;
              std::string selectedStage;
              std::string selectedSegment;
              int modeFlag = 1; //hard code this to only read from the segments.json
              int rows = 0; //hard code to zero in case we look too early
              void updateJsonStringList();
              json readFromJson(std::string identifier);
              //std::vector<std::string> jsonStringList;
              QStringList jsonStringList;
              json newJson;
          
              int rowCount(const QModelIndex &parent = QModelIndex()) const override;
              QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
              bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) const;
              QStringList getJsonSubdata(std::string jsonIndex);
          };
          

          I pared this down a bunch to remove unrelated stuff, but here's the source, jsonListModel.cpp:

          #include <QDebug>
          #include <iostream>
          #include <fstream>
          #include <QStringList>
          #include <experimental/filesystem>
          
          JsonListModel::JsonListModel(std::string newJsonIdentifier, QObject *parent) : QStringListModel(parent)
          {
              jsonIdentifier = newJsonIdentifier;
              updateJsonStringList();
              //this populates the stringlist as soon as the constructor is called
          }
          
          int JsonListModel::rowCount(const QModelIndex & /*parent*/) const
          {
              return rows;
          }
          
          QVariant JsonListModel::data(const QModelIndex &index, int role) const 
          {
              int row = index.row();
              if(role == Qt::DisplayRole)
              {
                  return jsonStringList.at(row);
              }
              return QVariant();
          }
          
          void JsonListModel::updateJsonStringList()
          {   
             //A bunch of stuff happens here to fill up jsonStringList with data from the json files
              setStringList(jsonStringList);
          }
          

          Then in the header of the widget that holds the jsonStringListModels:

          //QListViews
              QListView* jsonList;
              QListView* cycleList;
              QListView* stageList;
              QListView* segmentList;
          
              //SegmentTableModels
              JsonListModel* jsonCycleListModel;
              JsonListModel* jsonStageListModel;
              JsonListModel* jsonSegmentListModel;
          
              JsonListModel* cycleListModel;
              JsonListModel* stageListModel;
              JsonListModel* segmentListModel;
          

          And the source of that widget:

          //Models
              jsonCycleListModel = new JsonListModel("jsonCycles");
              jsonStageListModel = new JsonListModel("jsonStages");
              jsonSegmentListModel = new JsonListModel("jsonSegments");
          
              cycleListModel = new JsonListModel("cycles");
              stageListModel = new JsonListModel("stages");
              segmentListModel = new JsonListModel("segments");
          
          //QListViews
              jsonList = new QListView(this);
              jsonList->setDragEnabled(true);
              jsonList->setAcceptDrops(true);
              jsonList->setDropIndicatorShown(true);
              jsonList->setModel(jsonCycleListModel);
              jsonList->setStyleSheet(
                  "QListView { font-size: 10pt; font-weight: bold; }"
                  "QListView::item { background-color: #E74C3C; padding: 10%;"
                  "border: 1px solid #C0392B; }"
                  "QListView::item::hover { background-color: #C0392B }");
          

          I have four QListViews in here, but they're all basically the same, so I don't see the need in posting all of them.

          So now that I've been looking at this a little closer, I suspect the issue is with my call of setStringList(jsonStringList); - it feels like I should be appending to the model's stringList? I guess I'm not clear if that call attaches my stringlist to the model, or if it just copies the contents over. The other possible issue is what I mentioned in the first post, that I'm missing an override of a virtual function somewhere. I've been doing a bunch of document reading, and there's a bunch of stuff about MIME data, but my understanding was that that would be taken care of by the QStringListModel already.

          VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #4

          @Kelenyche said in QStringListModel Subclass Drag and Drop:

          setStringList(jsonStringList)

          You didn't show us that method.

          My suggestion would be: don't remplement anything. Just add some code to populate the model but let the base class do the dirty work for you. Delete JsonListModel::rowCount and JsonListModel::data altogether

          P.S.

          • Qt has json reading and writing classes so a bit odd that you used an external library for it

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          1 Reply Last reply
          2
          • K Offline
            K Offline
            Kelenyche
            wrote on last edited by Kelenyche
            #5

            void QStringListModel::setStringList(const QStringList &strings) is already a member of QStringListModel, I didn't do anything to it.
            https://doc.qt.io/qt-5/qstringlistmodel.html#setStringList

            You might be right about my reimplementation of data and rowCount - I thought I read somewhere that if you subclass, those two are definitely required to be reimplemented, but I might have confused myself reading too many different things. It makes sense, since I'm using setStringList. I'll give that a go.

            PS
            Thanks for the info on jsons! I didn't know Qt had json stuff - I've used this library before so I grabbed it before I thought to check qt for it. :)

            1 Reply Last reply
            0
            • K Offline
              K Offline
              Kelenyche
              wrote on last edited by
              #6

              Yep, removing my reimplementation of rowCount and data fixed the problem. Like I said, I think I saw somewhere that any subclass needed those functions, but it must have been talking about QAbstractListModel, and at that point I had been reading a LOT of documentation and got them mixed up. :) Thank you for the suggestion!

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved