Solved 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!
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; }
-
@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.
-
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.
-
@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?
-
@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.
-
Thanks, you helped me a lot!
-
Hi,
Isn't that what the application QML object provides already ?
-
@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.