ComboBox together with QStringListModel
-
Struggeling while trying to an editable ComboBox for QML. See also https://forum.qt.io/topic/120952/using-editable-combobox
The purpose is described in the other post.
I came across QStringListModel which seem to fulfill my purposes also in view of being able to store and set the actual entries in combination with QSettings. Also I am more comfortable with Qt for C++
However, the combination is not as easy to implement as initially thought.
I have used a Qt Quick template to create a minimal application.
The .pro file from template
QT += quick CONFIG += c++11 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
The main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QStringListModel> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); qmlRegisterType <QStringListModel> ( "StringListModel", 1, 0, "QStringListModel" ); QStringListModel lstModel; lstModel.setStringList( QStringList() << "Test1" << "Test2"); qDebug() << lstModel.stringList(); engine.rootContext()->setContextProperty ("myLstModel", &lstModel ); engine.load(url); return app.exec(); }
The main.qml
import QtQuick 2.15 import QtQuick.Controls 2.15 import StringListModel 1.0 ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Scroll") ComboBox { id: firstComboBox model: StringListModel } ComboBox { id: secondComboBox anchors.top: firstComboBox.bottom model: myLstModel } ComboBox { id: thirdComboBox anchors.top: secondComboBox.bottom model: myLstModel.stringList } }
There already some remains of my struggle to get the initial entries from QStringListModel into the ComboBox in QML. None of these attempts worked. The ComboBox in the middle has at least the right number of entries, but they are all empty.
Has anyone an idea what I am missing?
-
@koahnig The middle approach is as you say closest to working.
If you wanted to use your first approach, based on
StringListModel
as exposure ofQStringListModel
to QML, you would have to instantiate it somewhere. You are assigning the typeStringListModel
to yourmodel
property rather than an instance of it.However, assuming you go for the context property approach I think you are at least missing setting the
textRole
insecondComboBox
. Try setting:... model: myLstModel textRole: "display" }
-
-
The only solution I found is with inheriting from QStringListModel.
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QSettings> #include "MyStringListModel.h" #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); qmlRegisterType <MyStringListModel> ( "StringListModel", 1, 0, "MyStringListModel" ); MyStringListModel lstModel; QSettings settings ( "TestQml.ini", QSettings::IniFormat ); lstModel.readSettings(&settings, "Components"); if ( lstModel.stringList().size() < 1 ) { lstModel.setStringList( QStringList() << "Test 1" << "Test 2"); lstModel.setCurrentIndex(0); } qDebug() << lstModel.stringList(); engine.rootContext()->setContextProperty ("myLstModel", &lstModel ); engine.load(url); return app.exec(); }
MyStringListModel.h
#ifndef MYSTRINGLISTMODEL_H #define MYSTRINGLISTMODEL_H #include <QObject> #include <QStringListModel> class QSettings; class MyStringListModel : public QStringListModel { Q_OBJECT Q_PROPERTY (int CurrentIndex READ getCurrentIndex WRITE setCurrentIndex NOTIFY sigCurrentIndex ) int CurrentIndex; public: MyStringListModel(); int getCurrentIndex () const; void setCurrentIndex ( int indx ); void readSettings ( QSettings *settings, const QString & category ); void writeSettings ( QSettings *settings, const QString & category ) const; public slots: void addNewText ( QString editText ); void sltEditText ( QString editText ); signals: void sigCurrentIndex(); }; #endif // MYSTRINGLISTMODEL_H
MyStringListModel.cpp
#include "MyStringListModel.h" #include <QSettings> MyStringListModel::MyStringListModel() : CurrentIndex ( 0 ) { } int MyStringListModel::getCurrentIndex() const { return CurrentIndex; } void MyStringListModel::setCurrentIndex(int indx) { CurrentIndex = indx; } void MyStringListModel::addNewText(QString editText) { const QStringList &lst = stringList(); for ( int i = 0; i < lst.size(); ++i ) { if ( editText == lst[i] ) return; } setStringList ( stringList() << editText ); setCurrentIndex ( rowCount() - 1 ); } void MyStringListModel::readSettings(QSettings *settings, const QString &category) { settings->beginGroup(category); QStringList lst = settings->value("Entries", QStringList()).toStringList(); setStringList ( lst ); int currentIndex = settings->value("CurrentPosition", 0 ).toInt(); setCurrentIndex(currentIndex); settings->endGroup(); } void MyStringListModel::writeSettings(QSettings *settings, const QString &category) const { settings->beginGroup(category); settings->setValue("Entries", stringList()); settings->setValue("CurrentPosition", CurrentIndex ); settings->endGroup(); } void MyStringListModel::sltEditText(QString editText) { addNewText ( editText ); QSettings settings ( "TestQml.ini", QSettings::IniFormat ); writeSettings(&settings, "Components"); }
main.qml
import QtQuick 2.15 import QtQuick.Controls 2.15 import StringListModel 1.0 ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Scroll") Frame { ComboBox { id: secondComboBox editable: true model: myLstModel currentIndex: model.CurrentIndex textRole: "display" onAccepted: { if (find(editText) === -1) model.addNewText ( editText ) } } } }