Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Set ListView Model from C++ Code
Forum Updated to NodeBB v4.3 + New Features

Set ListView Model from C++ Code

Scheduled Pinned Locked Moved Solved QML and Qt Quick
8 Posts 3 Posters 3.8k 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.
  • D Offline
    D Offline
    deleted415
    wrote on 13 Jun 2017, 18:17 last edited by
    #1

    Hi, I just started using Qt. So please bare with me.
    I'd like to use Qml QuickControls and mix it with C++ Code. My first project is a ListView with a Button underneath. The Button opens a file dialog and I can select a .csv table which contains a list of names and some information on them.

    The problem is, I have no idea, how to update the model of the listview from the C++ code. I'm using QQmlApplicationEngine (code follows below).

    So my question is basically how do I do this?
    I read some tutorials and info pages but there a lot of really confusing stuff the I can't get my head around.

    • How do I get access to QML objects? Have I done that correctly?
    • Do I always just use QQuickItem für every QML object? Why is there no corresponding ListView C++ class?
    • In some tutorials I read about setting context properties. But I couldn't quite translate that to the QQmlApplicationEngine approach. I tried using a property. Is that even possible?
    • Is it correct how I set up the Guest class with it's properties? And can cast that to a QVariant and set the views property?

    If the following is all totally wrong, could you please show me a good tutorial or give me some hints and tips? In general Qt seems to be very well documented, but I find the whole pure qml, QWidgets and mixing of everything hard to get my head around. Any help is appreciated, thanks!

    So far I have tried something like this:

    main.qml:

    import QtQuick 2.7
    import QtQuick.Controls 2.2
    import QtQuick.Layouts 1.3
    import QtQuick.Dialogs 1.2
    
    ApplicationWindow {
        id: applicationWindow
        objectName: "App"
        visible: true
        width: 640
        height: 480
        title: qsTr("Rosinenpicker")
    
        ColumnLayout {
            id: columnLayout
            anchors.rightMargin: 40
            anchors.leftMargin: 40
            anchors.bottomMargin: 40
            anchors.topMargin: 40
            anchors.fill: parent
    
    
            TextField {
                id: guestName
                text: qsTr("Text Field")
                Layout.preferredHeight: 50
                Layout.fillWidth: true
            }
    
            ListView {
                property ListModel guestModel
    
                id: guestList
                objectName: "guestView"
                Layout.fillHeight: true
                Layout.fillWidth: true
                model: guestModel
                delegate: guestDelegate
            }
    
            Component {
                id: guestDelegate
                Rectangle {
                    width: 50
                    height: 50
                    color: "red"
                    Text {
                        text: modelData
                    }
                }
            }
    
            Button {
                id: button
                width: 50
                text: qsTr("Open File")
                Layout.preferredWidth: 100
                Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
    
                onClicked: fileDialog.visible = true;
            }
    
            FileDialog {
                id: fileDialog
                title: "Please choose a valid guestlist file"
                objectName: "fileDialog"
                nameFilters: ["Valid guestlist files (*.csv)"]
                selectMultiple: false
    
                signal fileSelected(url path)
    
                onAccepted: fileSelected(fileDialog.fileUrl)
            }
        }
    }
    

    main.cpp:

    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        auto root = engine.rootObjects().first();
        auto guestView = root->findChild<QQuickItem *>("guestView");
        auto fileDialog = root->findChild<QObject*>("fileDialog");
    
        CsvParser parser(guestView);
        QObject::connect(fileDialog, SIGNAL(fileSelected(QUrl)), &parser, SLOT(loadData(QUrl)));
    
        return app.exec();
    }
    
    #include "csvparser.h"
    #include "guest.h"
    
    #include <QtDebug>
    #include <QFile>
    
    CsvParser::CsvParser(QQuickItem *view)
    {
        this->view = view;
    }
    
    void CsvParser::loadData(QUrl path)
    {
        friendList.clear();
        guestList.clear();
    
        QFile file(path.toLocalFile());
        file.open(QIODevice::ReadOnly);
    
        // ignore first lines
        for(int i=0; i<3; i++)
            file.readLine();
    
        // parse file
        while(!file.atEnd()) {
            auto rowCells = file.readLine().split(',');
    
            if(rowCells.size() != 6)
                continue;
    
            checkedAdd(friendList, rowCells[0], rowCells[1]);
            checkedAdd(guestList, rowCells[3], rowCells[4]);
        }
    
        // set property of listview?
        view->setProperty("guestModel", QVariant::fromValue(guestList));
    }
    
    void CsvParser::checkedAdd(QList<QObject*> &list, QString name, QString familyName)
    {
        if(name == "" && familyName == "")
            return;
    
        list.append(new Guest(name, familyName));
    }
    

    guest.h:

    #ifndef GUEST_H
    #define GUEST_H
    
    #include <QObject>
    
    class Guest : public QObject
    {
    public:
        Q_OBJECT
    
        Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
        Q_PROPERTY(QString fName READ fName WRITE setfName NOTIFY fNameChanged)
    
    public:
        Guest(QString name, QString fName);
    
        QString name();
        QString fName();
    
        void setName(QString name);
        void setfName(QString fName);
    
    signals:
        void nameChanged();
        void fNameChanged();
    
    private:
        QString m_name, m_fName;
    };
    
    #endif // GUEST_H
    

    guest.cpp:

    Guest::Guest(QString name, QString fName)
    {
        setName(name);
        setfName(fName);
    }
    
    QString Guest::name()
    {
        return m_name;
    }
    
    QString Guest::fName()
    {
        return m_fName;
    }
    
    void Guest::setName(QString name)
    {
        m_name = name;
    }
    
    void Guest::setfName(QString fName)
    {
        m_fName = fName;
    }
    
    E 1 Reply Last reply 14 Jun 2017, 04:47
    0
    • D deleted415
      13 Jun 2017, 18:17

      Hi, I just started using Qt. So please bare with me.
      I'd like to use Qml QuickControls and mix it with C++ Code. My first project is a ListView with a Button underneath. The Button opens a file dialog and I can select a .csv table which contains a list of names and some information on them.

      The problem is, I have no idea, how to update the model of the listview from the C++ code. I'm using QQmlApplicationEngine (code follows below).

      So my question is basically how do I do this?
      I read some tutorials and info pages but there a lot of really confusing stuff the I can't get my head around.

      • How do I get access to QML objects? Have I done that correctly?
      • Do I always just use QQuickItem für every QML object? Why is there no corresponding ListView C++ class?
      • In some tutorials I read about setting context properties. But I couldn't quite translate that to the QQmlApplicationEngine approach. I tried using a property. Is that even possible?
      • Is it correct how I set up the Guest class with it's properties? And can cast that to a QVariant and set the views property?

      If the following is all totally wrong, could you please show me a good tutorial or give me some hints and tips? In general Qt seems to be very well documented, but I find the whole pure qml, QWidgets and mixing of everything hard to get my head around. Any help is appreciated, thanks!

      So far I have tried something like this:

      main.qml:

      import QtQuick 2.7
      import QtQuick.Controls 2.2
      import QtQuick.Layouts 1.3
      import QtQuick.Dialogs 1.2
      
      ApplicationWindow {
          id: applicationWindow
          objectName: "App"
          visible: true
          width: 640
          height: 480
          title: qsTr("Rosinenpicker")
      
          ColumnLayout {
              id: columnLayout
              anchors.rightMargin: 40
              anchors.leftMargin: 40
              anchors.bottomMargin: 40
              anchors.topMargin: 40
              anchors.fill: parent
      
      
              TextField {
                  id: guestName
                  text: qsTr("Text Field")
                  Layout.preferredHeight: 50
                  Layout.fillWidth: true
              }
      
              ListView {
                  property ListModel guestModel
      
                  id: guestList
                  objectName: "guestView"
                  Layout.fillHeight: true
                  Layout.fillWidth: true
                  model: guestModel
                  delegate: guestDelegate
              }
      
              Component {
                  id: guestDelegate
                  Rectangle {
                      width: 50
                      height: 50
                      color: "red"
                      Text {
                          text: modelData
                      }
                  }
              }
      
              Button {
                  id: button
                  width: 50
                  text: qsTr("Open File")
                  Layout.preferredWidth: 100
                  Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
      
                  onClicked: fileDialog.visible = true;
              }
      
              FileDialog {
                  id: fileDialog
                  title: "Please choose a valid guestlist file"
                  objectName: "fileDialog"
                  nameFilters: ["Valid guestlist files (*.csv)"]
                  selectMultiple: false
      
                  signal fileSelected(url path)
      
                  onAccepted: fileSelected(fileDialog.fileUrl)
              }
          }
      }
      

      main.cpp:

      int main(int argc, char *argv[])
      {
          QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
          QGuiApplication app(argc, argv);
      
          QQmlApplicationEngine engine;
          engine.load(QUrl(QLatin1String("qrc:/main.qml")));
          if (engine.rootObjects().isEmpty())
              return -1;
      
          auto root = engine.rootObjects().first();
          auto guestView = root->findChild<QQuickItem *>("guestView");
          auto fileDialog = root->findChild<QObject*>("fileDialog");
      
          CsvParser parser(guestView);
          QObject::connect(fileDialog, SIGNAL(fileSelected(QUrl)), &parser, SLOT(loadData(QUrl)));
      
          return app.exec();
      }
      
      #include "csvparser.h"
      #include "guest.h"
      
      #include <QtDebug>
      #include <QFile>
      
      CsvParser::CsvParser(QQuickItem *view)
      {
          this->view = view;
      }
      
      void CsvParser::loadData(QUrl path)
      {
          friendList.clear();
          guestList.clear();
      
          QFile file(path.toLocalFile());
          file.open(QIODevice::ReadOnly);
      
          // ignore first lines
          for(int i=0; i<3; i++)
              file.readLine();
      
          // parse file
          while(!file.atEnd()) {
              auto rowCells = file.readLine().split(',');
      
              if(rowCells.size() != 6)
                  continue;
      
              checkedAdd(friendList, rowCells[0], rowCells[1]);
              checkedAdd(guestList, rowCells[3], rowCells[4]);
          }
      
          // set property of listview?
          view->setProperty("guestModel", QVariant::fromValue(guestList));
      }
      
      void CsvParser::checkedAdd(QList<QObject*> &list, QString name, QString familyName)
      {
          if(name == "" && familyName == "")
              return;
      
          list.append(new Guest(name, familyName));
      }
      

      guest.h:

      #ifndef GUEST_H
      #define GUEST_H
      
      #include <QObject>
      
      class Guest : public QObject
      {
      public:
          Q_OBJECT
      
          Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
          Q_PROPERTY(QString fName READ fName WRITE setfName NOTIFY fNameChanged)
      
      public:
          Guest(QString name, QString fName);
      
          QString name();
          QString fName();
      
          void setName(QString name);
          void setfName(QString fName);
      
      signals:
          void nameChanged();
          void fNameChanged();
      
      private:
          QString m_name, m_fName;
      };
      
      #endif // GUEST_H
      

      guest.cpp:

      Guest::Guest(QString name, QString fName)
      {
          setName(name);
          setfName(fName);
      }
      
      QString Guest::name()
      {
          return m_name;
      }
      
      QString Guest::fName()
      {
          return m_fName;
      }
      
      void Guest::setName(QString name)
      {
          m_name = name;
      }
      
      void Guest::setfName(QString fName)
      {
          m_fName = fName;
      }
      
      E Offline
      E Offline
      Eeli K
      wrote on 14 Jun 2017, 04:47 last edited by Eeli K
      #2

      @Rulpi said in Set ListView Model from C++ Code:

      Hi, I just started using Qt. So please bare with me.
      I'd like to use Qml QuickControls and mix it with C++ Code. My first project is a ListView with a Button underneath. The Button opens a file dialog and I can select a .csv table which contains a list of names and some information on them.

      The problem is, I have no idea, how to update the model of the listview from the C++ code. I'm using QQmlApplicationEngine (code follows below).

      So my question is basically how do I do this?
      I read some tutorials and info pages but there a lot of really confusing stuff the I can't get my head around.

      • How do I get access to QML objects? Have I done that correctly?
      • Do I always just use QQuickItem für every QML object? Why is there no corresponding ListView C++ class?
      • In some tutorials I read about setting context properties. But I couldn't quite translate that to the QQmlApplicationEngine approach. I tried using a property. Is that even possible?
      • Is it correct how I set up the Guest class with it's properties? And can cast that to a QVariant and set the views property?

      If the following is all totally wrong, could you please show me a good tutorial or give me some hints and tips? In general Qt seems to be very well documented, but I find the whole pure qml, QWidgets and mixing of everything hard to get my head around. Any help is appreciated, thanks!

      Usually and in general you don't need to mix QWidgets and QML. And you haven't done it in your code, you use only non-widget part of C++ Qt. You're just trying to do too much in C++. Usually and in general you shouldn't handle the QML UI in C++. You should write the C++ classes like it was a backend non-UI library which you control from QML. This part:

      auto root = engine.rootObjects().first();
          auto guestView = root->findChild<QQuickItem *>("guestView");
          auto fileDialog = root->findChild<QObject*>("fileDialog");
      
          CsvParser parser(guestView);
          QObject::connect(fileDialog, SIGNAL(fileSelected(QUrl)), &parser, SLOT(loadData(QUrl)));
      

      is unnecessary. You just have to create the CsvParser and set it as a context property (let's call it csvParser). CsvParser shouldn't know the guestView. You have also to set the view's C++ model object as a context property (let's call it guestModel). In this case you can use QStringListModel.

      Instead of

      signal fileSelected(url path)
      onAccepted: fileSelected(fileDialog.fileUrl)
      

      you should have

      onAccepted: {csvParser.loadData(fileDialog.fileUrl)}
      

      Instead of

      // set property of listview?
      view->setProperty("guestModel", QVariant::fromValue(guestList));
      

      you should just modify the model in C++, in this case using guestModel.setStringList(). Then, if you have removed the unnecessary

      property ListModel guestModel
      

      from the list view it should be updated automatically.

      This doesn't necessarily work out of the box, I just tried to find most of the relevant pieces. Feel free to ask for clarifications.

      D 1 Reply Last reply 14 Jun 2017, 09:01
      2
      • E Offline
        E Offline
        Eeli K
        wrote on 14 Jun 2017, 05:10 last edited by
        #3

        You can use QStringListModel for quick testing, but you may want to create a more complicated model for your data. I didn't notice at first that you have several text fields. There are many possibilities for a C++ model and it's necessary trivial. Try to get the architecture working first.

        1 Reply Last reply
        0
        • E Eeli K
          14 Jun 2017, 04:47

          @Rulpi said in Set ListView Model from C++ Code:

          Hi, I just started using Qt. So please bare with me.
          I'd like to use Qml QuickControls and mix it with C++ Code. My first project is a ListView with a Button underneath. The Button opens a file dialog and I can select a .csv table which contains a list of names and some information on them.

          The problem is, I have no idea, how to update the model of the listview from the C++ code. I'm using QQmlApplicationEngine (code follows below).

          So my question is basically how do I do this?
          I read some tutorials and info pages but there a lot of really confusing stuff the I can't get my head around.

          • How do I get access to QML objects? Have I done that correctly?
          • Do I always just use QQuickItem für every QML object? Why is there no corresponding ListView C++ class?
          • In some tutorials I read about setting context properties. But I couldn't quite translate that to the QQmlApplicationEngine approach. I tried using a property. Is that even possible?
          • Is it correct how I set up the Guest class with it's properties? And can cast that to a QVariant and set the views property?

          If the following is all totally wrong, could you please show me a good tutorial or give me some hints and tips? In general Qt seems to be very well documented, but I find the whole pure qml, QWidgets and mixing of everything hard to get my head around. Any help is appreciated, thanks!

          Usually and in general you don't need to mix QWidgets and QML. And you haven't done it in your code, you use only non-widget part of C++ Qt. You're just trying to do too much in C++. Usually and in general you shouldn't handle the QML UI in C++. You should write the C++ classes like it was a backend non-UI library which you control from QML. This part:

          auto root = engine.rootObjects().first();
              auto guestView = root->findChild<QQuickItem *>("guestView");
              auto fileDialog = root->findChild<QObject*>("fileDialog");
          
              CsvParser parser(guestView);
              QObject::connect(fileDialog, SIGNAL(fileSelected(QUrl)), &parser, SLOT(loadData(QUrl)));
          

          is unnecessary. You just have to create the CsvParser and set it as a context property (let's call it csvParser). CsvParser shouldn't know the guestView. You have also to set the view's C++ model object as a context property (let's call it guestModel). In this case you can use QStringListModel.

          Instead of

          signal fileSelected(url path)
          onAccepted: fileSelected(fileDialog.fileUrl)
          

          you should have

          onAccepted: {csvParser.loadData(fileDialog.fileUrl)}
          

          Instead of

          // set property of listview?
          view->setProperty("guestModel", QVariant::fromValue(guestList));
          

          you should just modify the model in C++, in this case using guestModel.setStringList(). Then, if you have removed the unnecessary

          property ListModel guestModel
          

          from the list view it should be updated automatically.

          This doesn't necessarily work out of the box, I just tried to find most of the relevant pieces. Feel free to ask for clarifications.

          D Offline
          D Offline
          deleted415
          wrote on 14 Jun 2017, 09:01 last edited by
          #4

          @Eeli-K
          Thanks for that very helpful answer. It helps a lot in understanding what's happening.

          You just have to create the CsvParser and set it as a context property (let's call it csvParser). CsvParser shouldn't know the guestView. You have also to set the view's C++ model object as a context property (let's call it guestModel).

          How would I do that? Could you maybe give me a minimal example of this?

          E 1 Reply Last reply 14 Jun 2017, 09:39
          0
          • D deleted415
            14 Jun 2017, 09:01

            @Eeli-K
            Thanks for that very helpful answer. It helps a lot in understanding what's happening.

            You just have to create the CsvParser and set it as a context property (let's call it csvParser). CsvParser shouldn't know the guestView. You have also to set the view's C++ model object as a context property (let's call it guestModel).

            How would I do that? Could you maybe give me a minimal example of this?

            E Offline
            E Offline
            Eeli K
            wrote on 14 Jun 2017, 09:39 last edited by
            #5

            @Rulpi See the simple examples in http://doc.qt.io/qt-5/qtqml-cppintegration-contextproperties.html. Or this:

                QApplication* app{new QApplication(argc, argv)};
                QQmlApplicationEngine* engine{new QQmlApplicationEngine()};
                QQmlContext* ctx{engine->rootContext()};
                ctx->setContextProperty("qApplication", app);
            

            And in QML you can use the object named qApplication everywhere, for example:

            Text {
                text: qApplication.applicationDisplayName //applicationDisplayName is a property of QApplication
            }
            

            Context property is good for objects which exist for the whole lifetime of the qml engine.

            1 Reply Last reply
            1
            • D Offline
              D Offline
              deleted415
              wrote on 14 Jun 2017, 13:49 last edited by
              #6

              Thanks, you helped me a lot!

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on 14 Jun 2017, 21:45 last edited by
                #7

                Hi,

                Isn't that what the application QML object provides already ?

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

                E 1 Reply Last reply 15 Jun 2017, 09:45
                0
                • SGaistS SGaist
                  14 Jun 2017, 21:45

                  Hi,

                  Isn't that what the application QML object provides already ?

                  E Offline
                  E Offline
                  Eeli K
                  wrote on 15 Jun 2017, 09:45 last edited by
                  #8

                  @SGaist QApplication was just my example. Only part or it is copied from real code, maybe it had something which application QML object doesn't have, I don't remember.

                  1 Reply Last reply
                  0

                  1/8

                  13 Jun 2017, 18:17

                  • Login

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