2

I have a class called Data, and this class has a function with a loop that reads data from a device. The loop never stops reading data.

For the sake of this example, lets say that class Data has the following code:

class Data : public QObject
{
    Q_OBJECT

public:
    explicit Data(QObject *parent = nullptr){
        genData();
    }

    //the job of this function is to always get data
    void genData(){            
       while(true){
         m_number++;
    //somehow keep updating the value in QML and keep doing it?
       }    
    }

private:
    int m_number = 0;
};

So, what I need to do is be able to display m_number in the main.qml and update the value in the QML UI every time it changes in C++. In this case, the loop is incrementing the value of m_number.

I have some ideas of what I have to do to make this work, but I'm not positive. 1. I know I have to run the function in a different thread so that it doesnt block the rest of the program, and I was able to accomplish that. 2. I know I have to implement Q_PROPERTY and I was able to implement it as well, but it only shows the first value in Data::m_number(the initialized value 0).

What I dont know is how to make everything interact together. I'm also not sure if there's anything else I need to implement. I'm sure there's a lot of things I'm not seeing, though. I've read the documentation but I was only able to understand up to the point I'm currently at.

I would appreciate help and sample code that anyone could provide me with. Thank you.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Claudio Tejada
  • 239
  • 1
  • 4
  • 13
  • 2
    I recently answered something similar: https://stackoverflow.com/questions/46738764/is-there-a-way-to-run-c-from-a-qml-workerscript/46739399#46739399 – dtech Nov 06 '17 at 02:51

1 Answers1

4

QThread is a class that manages the threads in low level, a better option is to use QThreadPool and QRunnable.

QRunnable is a class that has a method that is going to run on some thread, this class will pass an object of type Data and update the data through a setter using invokeMethod.

runnable.h

#ifndef RUNNABLE_H
#define RUNNABLE_H

#include <QObject>
#include <QRunnable>
#include <QThread>

class Runnable : public QRunnable
{
    int mNumber = 0;
    QObject *mReceiver;
    bool mRunning;
public:
    Runnable(QObject *receiver){
        mReceiver = receiver;
        mRunning = false;
    }
    void run(){
        mRunning = true;
        while(mRunning){
            mNumber++;
            QMetaObject::invokeMethod(mReceiver, "setNumber",
                                      Qt::QueuedConnection,
                                      Q_ARG(int, mNumber));
            QThread::msleep(10);
        }
    }
    bool isRunning() const{
        return mRunning;
    }
    void stop(){
        mRunning = false;
    }
};

#endif // RUNNABLE_H

One way of interacting C ++ with QML is through Q_PROPERTY, so it will be used in this case, in the class we will execute the Runnable when the start() method is called from QML, for this we will use Q_INVOKABLE.

data.h

#ifndef DATA_H
#define DATA_H

#include "runnable.h"

#include <QObject>
#include <QThreadPool>

class Data : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged)
public:
    explicit Data(QObject *parent = nullptr):QObject(parent){
        m_number = 0;
        runnable = new Runnable(this);
    }
    ~Data(){
        runnable->stop();
    }
    Q_INVOKABLE void start(){
        if(!runnable->isRunning())
            QThreadPool::globalInstance()->start(runnable);
    }
    int number() const{
        return m_number;
    }
public slots:
    void setNumber(int number){
        if(number == m_number)
            return;
        m_number = number;
        emit numberChanged(m_number);
    }
signals:
    void numberChanged(int);
private:
    int m_number;
    Runnable *runnable;
};

#endif // DATA_H

Then the class is registered in the main, and then we create an item where the connections will be made

main.cpp

#include "data.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>

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

    qmlRegisterType<Data>("com.example.data", 1, 0, "Data");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.4

import com.example.data 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Example")

    ColumnLayout {
        anchors.centerIn: parent
        Button {
            id: button
            Layout.fillWidth: true
            text: "start"
            onClicked: data.start()
        }

        Text {
            id: text1
            text: data.number
            horizontalAlignment: Text.AlignHCenter
            Layout.fillWidth: true
        }
    }

    Data{
        id: data
    }
}

The example can be found in the following link.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • `QThread::currentThread()->msleep(10);` -> `QThread::msleep(10);`. The compiler warns about unnecessary class instance when calling static method (depending on compiler settings of course). – Alexander V Nov 06 '17 at 02:24
  • Hi and thank you for your answer! I try to run your code so that I can learn from it, it compiles and evertyhing but when ever I click on the start button nothing seems to happen. I'm running this in Linux and Qt 5.9.2 if that matters. Thanks again. – Claudio Tejada Nov 06 '17 at 02:29
  • @ClaudioTejada I just executed it and I observe what you tell me, maybe I have modified something without realizing it, I am reviewing it, when I finish you notice, I apologize for the inconvenience. – eyllanesc Nov 06 '17 at 02:35
  • @eyllanesc Thank you so much for your help! It works perfectly! – Claudio Tejada Nov 06 '17 at 02:54
  • 1
    @ClaudioTejada The answer of dtech is also correct :P – eyllanesc Nov 06 '17 at 02:56