AbstractListModel not showing changes from C++ Concurrent function
-
Hello!
I am trying to create an interface for Wi-Fi using NetworkManager and Qt/QML for an automotive cluster.
The Wifi Network list is built using a custom list model which was coded by following this tutorial pretty closely, so it shows how the code works for the most part:
https://www.youtube.com/watch?v=9BcAYDlpuT8&t=1801s&ab_channel=QtThe process is:
- Run a system command for network manager to get the current available networks.
QStringList AccessPointList::getAccessPoints() { QProcess process; QStringList arguments1; arguments1 << "device" << "wifi" << "rescan"; process.start("/usr/bin/nmcli", arguments1, QIODevice::ReadOnly); process.waitForFinished(-1); QStringList arguments2; arguments2 << "-f" << "SSID,SIGNAL" << "device" << "wifi" << "list" << "ifname" << "mlan0"; process.start("/usr/bin/nmcli", arguments2, QIODevice::ReadOnly); process.waitForFinished(-1); QString stdout = process.readAllStandardOutput(); QStringList lineList; QStringList stringList = stdout.split("\n", Qt::KeepEmptyParts); if(stringList.size() > 1) { stringList.removeFirst(); stringList.removeLast(); } return stringList; }
- Take the output of this command and parse it using the following buildList() function:
void AccessPointList::buildList() { qDebug() << "Build List."; clearItemList(); QStringList lineList = getAccessPoints(); QString currentString; QStringList lineItems; QThread::sleep(3); lineList.sort(Qt::CaseInsensitive); if(lineList.size() > 1) { for (int i = 0; i < lineList.size(); i++) { currentString = lineList.at(i); lineItems = currentString.split(" ", Qt::SkipEmptyParts); AccessPointItem myItem = {lineItems.at(0), lineItems.at(1).toInt()}; appendItem(myItem); } removeDuplicateItems(); } AccessPointItem otherItem = {"Join Other Network...", -1}; appendItem(otherItem); appendItem(otherItem); appendItem(otherItem); appendItem(otherItem); qDebug() << mItems.at(0).ssid; emit built(); }
- Add this list as a member of another class AccessPointModel which is invoked by QML inside a ListView item.
#ifndef ACCESSPOINTMODEL_H #define ACCESSPOINTMODEL_H #include <QAbstractListModel> class AccessPointList; class AccessPointModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(AccessPointList *list READ list WRITE setList CONSTANT) Q_PROPERTY(bool listBuilding READ getBuildStatus NOTIFY listStatusChanged) Q_PROPERTY(QString currentSsid READ getCurrentSsid NOTIFY currentSsidChanged) Q_PROPERTY(int currentSignal READ getCurrentSignal NOTIFY currentSignalChanged) public: explicit AccessPointModel(QObject *parent = nullptr); enum { SsidRole = Qt::UserRole, SignalRole }; // Basic functionality: int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // Editable: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; virtual QHash<int, QByteArray> roleNames() const override; AccessPointList *list() const; bool getBuildStatus(); QString getCurrentSsid(); void setCurrentSsid(QString ssid); int getCurrentSignal(); void setCurrentSignal(int signal); Q_INVOKABLE bool connectNetwork(QString ssid, QString password); Q_INVOKABLE void network_status(); private: AccessPointList *mList; bool m_listBuilding; QString m_ssid; int m_signal; signals: void listStatusChanged(); void currentSignalChanged(); void currentSsidChanged(); public slots: void setList(AccessPointList *list); void setBuildStatus(bool status); void building(); void built(); }; #endif // ACCESSPOINTMODEL_H
QML:
ScrollView { id: scrollView width: 400 height: 180 clip: true anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenterOffset: 30 ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.vertical.width: 10 palette.text: myList.mouseClicked? "#2AA9E0" : "black" onActiveFocusChanged:{drawer.interactive = !activeFocus;} ListView { id: myList model: AccessPointModel{id: model; list: accessPointList} spacing: 4 delegate: ItemDelegate
The issue I'm running into is with running the list building function concurrently.
In the constructor for AccessPointList I declare a QTimer which calls buildList() every 10 seconds.AccessPointList::AccessPointList(QObject *parent) : QObject{parent} { QTimer *timer = new QTimer(); timer->setInterval(10000); connect(timer, SIGNAL(timeout()), this, SIGNAL(building())); //connect(timer, SIGNAL(timeout()), this, SLOT(buildRun())); connect(timer, SIGNAL(timeout()), this, SLOT(buildList())); timer->start(); }
I have a "Join Other Networks" element that should always be in the list, should the user decide to manually join a network. (I have appended it to the list several times for debugging purposes).
When I run buildList() directly by the timer it works as expected (as seen in the picture above) but with the UI sometimes being held up.
Because this function can take some time and I don't want the UI to be held up by it, I have started calling it concurrently.When I run it with QtConcurrent::run() I can see the buildList function being executed through debug output in the console, but I cannot see the model in the UI being updated.
AccessPointList::AccessPointList(QObject *parent) : QObject{parent} { QTimer *timer = new QTimer(); timer->setInterval(10000); connect(timer, SIGNAL(timeout()), this, SIGNAL(building())); connect(timer, SIGNAL(timeout()), this, SLOT(buildRun())); //connect(timer, SIGNAL(timeout()), this, SLOT(buildList())); timer->start(); } void AccessPointList::buildRun() { qDebug() << "Build Run."; QtConcurrent::run(this, &AccessPointList::buildList); }
I am unsure why simply running buildList() in a concurrent thread stops the model from displaying the list as opposed to running it in the main thread through what is essentially the same mechanism.
Do you have any suggestions as to what is happening here?Cheers!