QML and QQuickWidget

1.6k views Asked by At

I am new to qml but I want to add a circle gauge to the QQuickWidget by referring to the dashboard of the QT example. Below is my codes.

guagetest.pro

QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets quickwidgets
CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    dashboard.qrc

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->quickWidget->setSource(QUrl("qrc:/qml/qml/test.qml"));
    ui->quickWidget->show();
}

MainWindow::~MainWindow()
{
    delete ui;
} 

test.qml

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4

Item {
    id: container
    width: parent.width
    height: parent.height
    anchors.centerIn: parent.Center

    Row {
        id: gaugeRow
        spacing: container.width * 0.2
        anchors.centerIn: parent

        CircularGauge {
            id: speedometer
            value: valueSource.kph
            anchors.verticalCenter: parent.verticalCenter
            maximumValue: 280
            // We set the width to the height, because the height will always be
            // the more limited factor. Also, all circular controls letterbox
            // their contents to ensure that they remain circular. However, we
            // don't want to extra space on the left and right of our gauges,
            // because they're laid out horizontally, and that would create
            // large horizontal gaps between gauges on wide screens.
            width: height
            height: container.height * 0.8

            style: DashboardGaugeStyle {}
        }
    }
}

DashboardGaugeStyle.qml

import QtQuick 2.2
import QtQuick.Controls.Styles 1.4

CircularGaugeStyle {
    tickmarkInset: toPixels(0.04)       // gauge graduation radius
    minorTickmarkInset: tickmarkInset
    labelStepSize: 20                   // gauge graduation text
    labelInset: toPixels(0.23)          // gauge graduation text position

    property real xCenter: outerRadius
    property real yCenter: outerRadius
    property real needleLength: outerRadius - tickmarkInset * 1.25
    property real needleTipWidth: toPixels(0.02)
    property real needleBaseWidth: toPixels(0.06)
    property bool halfGauge: false

    function toPixels(percentage) {
        return percentage * outerRadius;
    }

    function degToRad(degrees) {
        return degrees * (Math.PI / 180);
    }

    function radToDeg(radians) {
        return radians * (180 / Math.PI);
    }

    function paintBackground(ctx) {
        if (halfGauge) {
            ctx.beginPath();
            ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height / 2);
            ctx.clip();
        }

        ctx.beginPath();
        ctx.fillStyle = "black";
        ctx.ellipse(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = tickmarkInset;
        ctx.strokeStyle = "black";
        ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius -ctx.lineWidth / 2, 0, Math.PI * 2);
        ctx.stroke();

        ctx.beginPath();
        ctx.lineWidth = tickmarkInset / 2;
        ctx.strokeStyle = "#222";
        ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius -ctx.lineWidth / 2, 0, Math.PI * 2);
        ctx.stroke();

        ctx.beginPath();
        var gradient = ctx.createRadialGradient(xCenter, yCenter, 0, xCenter, yCenter, outerRadius * 1.5);
        gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0));
        gradient.addColorStop(0.7, Qt.rgba(1, 1, 1, 0.13));
        gradient.addColorStop(1, Qt.rgba(1, 1, 1, 1));
        ctx.fillStyle = gradient;
        ctx.arc(xCenter, yCenter, outerRadius - tickmarkInset, outerRadius - tickmarkInset, 0, Math.PI * 2);
        ctx.fill();
    }

    background: Canvas {
        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();
            paintBackground(ctx);
        }

        Text {
            id: speedText
            font.pixelSize: toPixels(0.3)
            text: kphInt
            color: "white"
            horizontalAlignment: Text.AlignRight
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.verticalCenter
            anchors.topMargin: toPixels(0.1)

            readonly property int kphInt: control.value
        }
        Text {
            text: "km/h"
            color: "white"
            font.pixelSize: toPixels(0.09)
            anchors.top: speedText.bottom
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    needle: Canvas {
        implicitWidth: needleBaseWidth
        implicitHeight: needleLength

        property real xCenter: width / 2
        property real yCenter: height / 2

        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();

            ctx.beginPath();
            ctx.moveTo(xCenter, height);
            ctx.lineTo(xCenter - needleBaseWidth / 2, height - needleBaseWidth / 2);
            ctx.lineTo(xCenter - needleTipWidth / 2, 0);
            ctx.lineTo(xCenter, yCenter - needleLength);
            ctx.lineTo(xCenter, 0);
            ctx.closePath();
            ctx.fillStyle = Qt.rgba(0.66, 0, 0, 0.66);
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(xCenter, height)
            ctx.lineTo(width, height - needleBaseWidth / 2);
            ctx.lineTo(xCenter + needleTipWidth / 2, 0);
            ctx.lineTo(xCenter, 0);
            ctx.closePath();
            ctx.fillStyle = Qt.lighter(Qt.rgba(0.66, 0, 0, 0.66));
            ctx.fill();
        }
    }

    foreground: null
}

When I compile, the following message appears.

qrc:/qml/qml/test.qml:9: TypeError: Cannot read property 'width' of null
qrc:/qml/qml/test.qml:10: TypeError: Cannot read property 'height' of null
qrc:/qml/qml/test.qml:20: ReferenceError: valueSource is not defined
qrc:/qml/qml/test.qml:11: TypeError: Cannot read property 'Center' of null

I want to know why that message comes out and how to solve it. and How can i change background color of QQuickWidget?

Please help me.

1

There are 1 answers

3
Kao On

Root object needs an initial size. And no need to center in parent, cause there is no parent. Let's say size is 500x300.

Item {
    id: container
    width: 500
    height: 300

Or if you don't want to give a constant size, but to make size exactly to fit content childrenRect can be used. Make sure your content size does not depend on root and has a valid width and height before use it. And it might cause "binding loop detected for width/height." warnings.

Item {
    id: container
    width: childrenRect.width
    height: childrenRect.height

And if you want your scene to resize respect to QQuickWidget's size dynamically set resize mode.

ui->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);

Let's get to coloring point. To change root item's color we can use Rectangle.color property. So change root object from Item to Rectangle. Let's make background red.

Rectangle {
    id: container
    width: childrenRect.width
    height: childrenRect.height
    color: "red"

Or if you want to change window color of QQuickWidget, set the palette. But since your scene going to cover it, I doubt that is what you need.

auto palette = ui->quickWidget->palette();
palette.setColor(QPalette::Window, QColor(Qt::red));
ui->quickWidget->setPalette(palette);

And you have one more problem:

qrc:/qml/qml/test.qml:20: ReferenceError: valueSource is not defined

I have no idea what valueSource is, either make sure you have it or get rid of it.