Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 of QStringListModel to QML, you would have to instantiate it somewhere. You are assigning the type StringListModel to your model 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 in secondComboBox. Try setting:

       ...
       model: myLstModel
       textRole: "display"
    }
    


  • @Bob64 said in ComboBox together with QStringListModel:

    @koahnig The middle approach is as you say closest to working.

       ...
       model: myLstModel
       textRole: "display"
    }
    

    This helps getting closer. Adding textRole displays all entries in the ComboBox.



  • 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 )
                }
            }
        }
    }
    

Log in to reply