Search code examples
c++qtlistviewqmlqstringlistmodel

How can I display a QStringList in C++ to a QML ListView


So I'm new to Qt and I'm trying to improve my C++ skills, so I decided to start a project where I can search items in a QStringList using a textfield. I got the search function working and I was able to move the result of the search into another QStringList, where I can use it to display to the user in a function that is declared as a "public slot".

The main Idea is that the list will auto update as soon as the user enters a character into the textfield, which it already does. So I managed to get the resulted list into the Slot function to be able to display a different list every time and character gets typed in the textfield.


In the function where I pass in the list of search results, I am trying to use this

m_context->setContextProperty("resultModel",QVariant::fromValue(m_resultList));

where resultModel is the name of my model in QML and m_resultList is where the results of the search are being stored, to display the list in the ListView. My program compiles but it crashes after I run it.

So, my true question is: Is there any way that I can display a C++ QStringList not in the main.cpp into a QML ListView?

The reason why I'm asking for it to not be in the main is because I tried to use that same line above in the main.cpp with a hard coded QStringList and the list was able to display, so there must be an issue with it not being in the main. Also because I would not be able to use the slot function in SearchClass to auto update.


main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "searchclass.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<SearchClass>("b9c.backend", 1, 0, "BackEnd");

    QQmlApplicationEngine engine;

    SearchClass obj;

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QQmlContext *context = engine.rootContext();

    obj.getContext(context);

    //the line below works if provided with a qstringlist

    //context->setContextProperty("resultModel", QVariant::fromValue(resultList));

    return app.exec();
}

SearchClass.h

#ifndef SEARCHCLASS_H
#define SEARCHCLASS_H

#include <QObject>
#include <QQmlContext>

class SearchClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString userSearch READ userSearch WRITE setUserSearch NOTIFY userSearchChanged)

public:
    SearchClass(QObject *parent = 0);

    QStringList resultList;

    QString userSearch();
    void setUserSearch(QString &userSearch);

    void getFilenameAndInput(QString inputString);
    QString CompareInputAndFilename(QString inputString, QString filename);
    QStringList getFileName();

    //get context
    void getContext(QQmlContext *context);

signals:
    void userSearchChanged();

public slots:
    void setUserSearch();

private:
    QStringList m_resultList;
    QString m_userSearch;
    QQmlContext* m_context;
};

#endif // SEARCHCLASS_H

SearchClass.cpp

#include "searchclass.h"
#include <QDebug>
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>


SearchClass::SearchClass(QObject *parent) : QObject(parent)
{
    connect(this, SIGNAL(userSearchChanged()), this, SLOT(setUserSearch()));
}

//the result should be displayed in this SLOT when ever the user types in a character into the textfield
void SearchClass::setUserSearch(){

    qDebug() << "SLOT: " << m_resultList;

//The line below makes the program crash. It works when implemented in the main.cpp
//    m_context->setContextProperty("resultModel", QVariant::fromValue(m_resultList));

}

QString SearchClass::userSearch()
{
    return m_userSearch;
}

void SearchClass::setUserSearch(QString &userSearch)
{
    if (userSearch == m_userSearch)
        return;

    m_userSearch = userSearch;

    qDebug() << "Input: " <<m_userSearch;

    getFilenameAndInput(m_userSearch);

    emit userSearchChanged();
}

QStringList SearchClass::getFileName(){

//Returns the items that will be searched for...

}

void SearchClass::getFilenameAndInput(QString inputString){

//Puts the search results into class variable m_resultList...

    m_resultList = resultList;

}

QString SearchClass::CompareInputAndFilename(QString inputString, QString filename){

//Search processing... 

}

//gets context to use setProperty in the above signal, but it crashes
void SearchClass::getContext(QQmlContext *context){

    m_context = context;

}

main.qml

import QtQuick 2.6
import QtQuick.Controls 2.0
import b9c.backend 1.0
import QtQuick.Window 2.2


ApplicationWindow {
    id: root
    width: 300
    height: 480
    visible: true
    BackEnd { id: backend }

    TextField {
        id: txtfield
        text: backend.userSearch
        placeholderText: qsTr("Search...")
        width: parent.width

        onTextChanged: backend.userSearch = text
    }

    ListView {
        id:view
        height: parent.height
        width: parent.width
        y: 5 + txtfield.height
        model: resultModel

        delegate: Rectangle {
            border.color: "lightblue"
            height: 25
            width: parent.width
            Text {
                anchors.centerIn: parent
                text: modelData
            }
        }
    }

}

Solution

  • You are doing it wrong. In every possible way. You even name getContext() the function that actually sets the context.

    m_resultList is never set to anything in that code you have provided. So there is no way to tell you why your application is crashing, because the actual data is a mystery.

    You also have a QObject derived class - your SearchClass. So you should expose that as a context property, and then have the string list interfaced to QML by being implemented as a Q_PROPERTY of SearchClass.

    Here is a simple example:

    // the equivalent of your SearchClass
    class Test : public QObject {
        Q_OBJECT
        Q_PROPERTY(QStringList model MEMBER m_model NOTIFY modelChanged)
        QStringList m_model;
      public slots:
        void setModel(QString m) {
          m_model = m.split(" ");
          modelChanged();
        }
      signals:
        void modelChanged();
    };
    
    // in main.cpp
      Test t;
      engine.rootContext()->setContextProperty("Test", &t);
    
    // in main.qml
    Column {
        TextField {
          onTextChanged: Test.setModel(text)
        }
        ListView {
          width: 200; height: 300
          spacing: 5    
          model: Test.model
          delegate: Rectangle {
            height: 25
            width: 200
            color: "lightgray"
            Text { text: modelData; anchors.centerIn: parent }
          }
        }
      }
    

    As you type the text string is sent to Test::setModel(), which then splits it into space separated tokens and sets the QStringList, which is used as a model source for the list view.