添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode . Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).

I've been working on a MQTT Dashboard, the idea is to cross compile it to run on a Raspberry Pi (topic for another time perhaps) but I've been running into some problems. I am familiar with C++ but this is the first time I try my hand at Qt and qml for a project, I've also been reading documentation and going through some tutorials but I think this case is too specific.

First, a bit of context: My code is heavily based on both the QT MQTT quick subscription example: https://doc.qt.io/QtMQTT/qtmqtt-quicksubscription-example.html and the Oscilloscope example: https://doc.qt.io/qt-5/qtcharts-qmloscilloscope-example.html . My application is split in pages in a swipe view.

My main idea was to add the missing update function from the Oscilloscope example's datasource.h and datasource.cpp to my qtmqttclient.h and qtmqttclient.cpp (see below) with some modifications because I am not generating my data, I get it from the mqtt message payload. The code compiles and I am able to run the application and receive messages from my MQTT broker (a Raspberry Pi) but I get this error message every time the update function should be called to replace the series in my Spline Chart:

Message Handling
Received 22.000000
Message Handling
Received 23.000000
Message Handling
Received 24.000000
qrc:/main.qml:82: TypeError: Property 'update' of object [object Object] is not a function

For reference, here are my source code files (please point out any noob mistakes as it helps speed up my learning process):

main.cpp:

#include "qmlmqttclient.h"
//#include <QGuiApplication>
#include <QtWidgets/QApplication>
#include <QQmlApplicationEngine>
#include <QLoggingCategory>
int main(int argc, char *argv[])
    qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    //QGuiApplication app(argc, argv);
    QApplication app(argc, argv);
    qmlRegisterType<QmlMqttClient>("MqttClient", 1, 0, "MqttClient");
    qmlRegisterUncreatableType<QmlMqttSubscription>("MqttClient", 1, 0, "MqttSubscription", QLatin1String("Subscriptions are read-only"));
    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);
    engine.load(url);
    if (engine.rootObjects().isEmpty())
            return -1;
    return app.exec();

I commented out the QGuiApplication, I read that QtCharts needs QApplication.
Here are my qtmqttclient.h and qtmqttclient.cpp files:

/****************************************************************************
** Contact: https://www.qt.io/licensing/
** This file is part of the examples of the Qt Toolkit.
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
****************************************************************************/
#ifndef QMLMQTTCLIENT_H
#define QMLMQTTCLIENT_H
#include <QtCore/QMap>
#include <QtCore/QObject>
#include <QtMqtt/QMqttClient>
#include <QtMqtt/QMqttSubscription>
#include <QtCharts/QAbstractSeries>
#include <QDateTime>
class QmlMqttClient;
QT_CHARTS_USE_NAMESPACE
class QmlMqttSubscription : public QObject
    Q_OBJECT
    Q_PROPERTY(QMqttTopicFilter topic MEMBER m_topic NOTIFY topicChanged)
public:
    QmlMqttSubscription(QMqttSubscription *s, QmlMqttClient *c, QObject *parent = 0);
    ~QmlMqttSubscription();
Q_SIGNALS:
    void topicChanged(QString);
    void messageReceived(const QString &msg);
public slots:
    void handleMessage(const QMqttMessage &qmsg);
    Q_INVOKABLE void update(QAbstractSeries *series);
private:
    Q_DISABLE_COPY(QmlMqttSubscription)
    QMqttSubscription *sub;
    QmlMqttClient *client;
    QMqttTopicFilter m_topic;
    //QDateTime current = QDateTime::currentDateTime();
    //qint64 timeRef = current.toSecsSinceEpoch();
    //qreal x = timeRef;
    qreal x = 0.0;
    QVector<QPointF> points;
    uint8_t samples = 0;
class QmlMqttClient : public QMqttClient
    Q_OBJECT
public:
    QmlMqttClient(QObject *parent = nullptr);
    Q_INVOKABLE QmlMqttSubscription *subscribe(const QString &topic);
    Q_INVOKABLE int publish(const QString &topic, const QString &message, int qos = 0, bool retain = false);
private:
    Q_DISABLE_COPY(QmlMqttClient)
#endif // QMLMQTTCLIENT_H

In this file I even made the update function invokable but it seems that it didn't do the trick...

/****************************************************************************
** Contact: https://www.qt.io/licensing/
** This file is part of the examples of the Qt Toolkit.
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
****************************************************************************/
#include "qmlmqttclient.h"
#include <QDebug>
#include <QtCharts/QXYSeries>
#include <QtCore/QtMath>
QT_CHARTS_USE_NAMESPACE
QmlMqttClient::QmlMqttClient(QObject *parent)
    : QMqttClient(parent)
QmlMqttSubscription* QmlMqttClient::subscribe(const QString &topic)
    auto sub = QMqttClient::subscribe(topic, 0);
    auto result = new QmlMqttSubscription(sub, this);
    return result;
QmlMqttSubscription::QmlMqttSubscription(QMqttSubscription *s, QmlMqttClient *c, QObject *parent)
    : QObject(parent),
      sub(s),
      client(c)
    connect(sub, &QMqttSubscription::messageReceived, this, &QmlMqttSubscription::handleMessage);
    m_topic = sub->topic();
QmlMqttSubscription::~QmlMqttSubscription()
void QmlMqttSubscription::update(QAbstractSeries *series)
    qDebug("Updating");
    if (series) {
        QXYSeries *xySeries = static_cast<QXYSeries *>(series);
        qDebug("Series: %s",xySeries->name().toLocal8Bit().data());
        xySeries->replace(points);
        points.clear();
void QmlMqttSubscription::handleMessage(const QMqttMessage &qmsg)
    qDebug()<<"Message Handling";
    emit messageReceived(qmsg.payload());
    x = x + 60;
    QString y_temp = qmsg.payload();
    qreal y = y_temp.toDouble();
    points.append(QPointF(x,y));
    qDebug("Received %f",y);
int QmlMqttClient::publish(const QString &topic, const QString &message, int qos, bool retain)
    auto result = QMqttClient::publish(QMqttTopicName(topic), message.toUtf8(), qos, retain);
    return result;

and here are my qml files and ui :
main.qml:

import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.VirtualKeyboard 2.4
import MqttClient 1.0
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtCharts 2.3
ApplicationWindow {
    id: window
    visible: true
    width: 480
    height: 320
    title: qsTr("MQTT Dashboard")
    property var tempSubscription: 0
    property var humtySubscription: 0
    property var heatIdxSubscription: 0
    property real time: 0.0
    MqttClient {
        id: client
        clientId: "Qt_MQTT_Dashboard"
        username: "pi"
        password: "D3vi@ntart"
        cleanSession: true
    SwipeView {
        id: swipeView
        anchors.fill: parent
        currentIndex: tabBar.currentIndex
        Page1Form {
            connectButton.onClicked: {
                client.hostname = hostnameField.text
                client.port = portField.text
                if (client.state === MqttClient.Connected) {
                    client.disconnectFromHost()
                    tempSubscription.destroy()
                    tempSubscription = 0
                } else {
                    client.connectToHost()
            function stateToString(value) {
                if (value === 0)
                    return "Disconnected"
                else if (value === 1)
                    return "Connecting"
                else if (value === 2)
                    return "Connected"
                    return "Unknown"
            status.text: "Status:" + stateToString(client.state)
        Page2Form {
            function tempMessage(payload)
                messageModel.insert(0, {"payload" : payload})
                if (messageModel.count >= 5){
                    messageModel.remove(4)
            subscribebutton.onClicked: {
                tempSubscription = client.subscribe(qsTr("/mqtt/temperature"))
                tempSubscription.messageReceived.connect(tempMessage)
            Timer {
                id: refreshTimer
                interval: 180000 // every 3 mins
                running: true
                repeat: true
                onTriggered: {
                    MqttSubscription.update(Page2Form.splineseries);
                    //dataSource.update(chartView.series(0));
                    //dataSource.update(chartView.series(1));
        Page3Form{
    footer: TabBar {
        id: tabBar
        currentIndex: swipeView.currentIndex
        TabButton {
            text: qsTr("Broker setup")
        TabButton {
            text: qsTr("View Data")
        TabButton {
            text: qsTr("Page 3")
    InputPanel {
        id: inputPanel
        z: 99
        y: window.height
        width: window.width
        states: State {
            name: "visible"
            when: inputPanel.active
            PropertyChanges {
                target: inputPanel
                y: window.height - inputPanel.height
        transitions: Transition {
            from: ""
            to: "visible"
            reversible: true
            ParallelAnimation {
                NumberAnimation {
                    properties: "y"
                    duration: 250
                    easing.type: Easing.InOutQuad

The Page2Form part is the one with problems, MqttSubscription.update(Page2Form.splineseries); is the referenced line, as you can see I left the similar function call from the oscilloscope example there. I also tried to call the function with argument splineseries since that is the name of the exported alias property I used in the ui file below:

Page1Form.ui.qml:

import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.1
import MqttClient 1.0
import QtCharts 2.3
Page {
    width: 480
    height: 320
    property alias messageView: messageView
    property alias messageModel: messageModel
    property alias subscribebutton: subscribebutton
    property alias splineseries: splineseries
    property alias spline: spline
    ChartView {
        enabled: client.state === MqttClient.Connected
        id: spline
        antialiasing: true
        theme: ChartView.ChartThemeDark
        title: "Temperature"
        x: 12
        y: 12
        width: 332
        height: 235
        SplineSeries {
            name: "tempSeries"
            id: splineseries
            useOpenGL: true
    ListModel {
        id: messageModel
    ListView {
        id: messageView
        x: 365
        y: 66
        model: messageModel
        height: 181
        width: 99
        Layout.columnSpan: 2
        Layout.fillHeight: true
        Layout.fillWidth: true
        clip: true
        delegate: Rectangle {
            width: messageView.width
            height: 30
            color: index % 2 ? "#DDDDDD" : "#888888"
            radius: 5
            Text {
                text: payload
                anchors.centerIn: parent
    Button {
        id: subscribebutton
        x: 382
        y: 12
        text: qsTr("Start")
        enabled: client.state === MqttClient.Connected

Now, to the best of my knowledge the only difference between my Subscription class and the Oscilloscope example's Datasource class is that it was registered with qmlRegisterUncreatableType in main.cpp and somehow that may prevent me from calling the update function...

Any pointers or references will be greatly appreciated!

@lopeztel Hi,

I don't think update is the issue.
What MqttSubscription refer to ? I don't see any place in your code where an instance is created whith this name.
Isn't that the problem ?

@Gojir4 Hi, thanks for having a look. As far as I know the class was created to just call QmlMqttSubscription* QmlMqttClient::subscribe somehow I think tempSubscription = client.subscribe(qsTr("/mqtt/temperature")) creates a subscription without creating an object of the class.

QmlMqttSubscription* QmlMqttClient::subscribe(const QString &topic)
    auto sub = QMqttClient::subscribe(topic, 0);
    auto result = new QmlMqttSubscription(sub, this);
    return result;
QmlMqttSubscription::QmlMqttSubscription(QMqttSubscription *s, QmlMqttClient *c, QObject *parent)
    : QObject(parent),
      sub(s),
      client(c)

and it is definitely working, adding to my overall confusion as to why update can't be called ....
Also, wouldn't defining the type as uncreatable prevent me from creating an object of the class in the traditional way?

@lopeztel said in Property of object is not a function:

tempSubscription = client.subscribe(qsTr("/mqtt/temperature"))

Ok, I didn't see it sorry. So should not it be :

tempSubscription.update(Page2Form.splineseries); 
			

@Gojir4 shoot, you're right, the subscribe function returns the actual subscription object! All along I had what I needed! Noob mistake, thanks a lot!
This is what happens when you spend hours staring at the screen ...

@lopeztel said in Property of object is not a function:

This is what happens when you spend hours staring at the screen ...

Sometimes when you have had too much to think you need a break and coffee.