Custom Dial Item using QQuickPaintedItem

Sometimes it is required for us to create some of the advanced controls apart from the basics which are provided by the QML. Qt provides a class called QQuickPaintedItem which can be used to create a custom item for the QML using QPainter API in the Scene Graph.

We can see that nowadays Qt and QML used widely in the automotive industry for both infotainment and instrument cluster. Currently Qt is offering custom Dial item only in the commercial version of Qt which is part of Enterprise controls.

In the past I have worked on similar kind of project in which I have created custom dial item using QDeclarativeItem in Qt 4. here in this post I am going to recreate similar kind of Dial item with minimal functionality.

We have to follow few steps to create a custom item and to use it with QML.

  1. Create a new class by sub classing QQuickPaintedItem.
  2. Define all the properties which has to be exposed to the QML as part of this Item.
  3. Re-implement the  void paint(QPainter *painter) method and perform all the drawing for our custom dial item.
  4. Expose the new Item to the QML by using qmlRegisterType 
  5. import our new module in QML file. Use this new item like any other item in QML

The dial item which I have created only has the background dial, tick marks and numbers. It does not provide the needle. For needle I am using an image which I am placing over my Dial item in the QML and set the rotation property for it.

Here is my implementation for the custom dial item.


#ifndef DIALITEM_H
#define DIALITEM_H

#include <QQuickPaintedItem>

class DialItem : public QQuickPaintedItem
    Q_PROPERTY(int startAngle READ getStartAngle WRITE setStartAngle NOTIFY startAngleChanged)
    Q_PROPERTY(qreal spanAngle READ getSpanAngle WRITE setSpanAngle NOTIFY spanAngleChanged)
    Q_PROPERTY(int dialWidth READ getDialWidth WRITE setDialWidth NOTIFY dialWidthChanged)
    Q_PROPERTY(QColor dialColor READ getDialColor WRITE setDialColor NOTIFY dialColorChanged)

    DialItem(QQuickItem *parent = 0);
    void paint(QPainter *painter);
    int getStartAngle() {return m_StartAngle;}
    qreal getSpanAngle() {return m_SpanAngle;}
    int getDialWidth() {return m_DialWidth;}
    QColor getDialColor() {return m_DialColor;}
    void setStartAngle(int angle) {m_StartAngle = angle; this->update();}
    void setSpanAngle(qreal angle) {m_SpanAngle = angle; this->update();}
    void setDialWidth(int angle) {m_DialWidth = angle; this->update();}
    void setDialColor(QColor color) {m_DialColor = color; this->update();}

    void startAngleChanged();
    void spanAngleChanged();
    void dialWidthChanged();
    void dialColorChanged();

    int m_StartAngle;
    qreal m_SpanAngle;
    int m_DialWidth;
    QColor m_DialColor;
#endif // DIALITEM_H


#include <QTime>
#include <QPainter>

#include "dialitem.h"

#define PI 3.14159265

DialItem::DialItem(QQuickItem *parent)
    : QQuickPaintedItem(parent)
    m_StartAngle = 0;
    m_SpanAngle = 180;
    m_DialWidth = 4;
    m_DialColor = QColor(0, 0, 0);
    connect(this, SIGNAL(startAngleChanged()), this, SLOT(update()));
    connect(this, SIGNAL(spanAngleChanged()), this, SLOT(update()));
    connect(this, SIGNAL(dialWidthChanged()), this, SLOT(update()));
    connect(this, SIGNAL(dialColorChanged()), this, SLOT(update()));

void DialItem::paint(QPainter *painter)
    QRectF rect = this->boundingRect();
    QPen pen = painter->pen();

    //Draw outer dial
    double startAngle = -90 - m_StartAngle;
    double spanAngle = 0 - m_SpanAngle;
    qreal offset = m_DialWidth / 2;
    painter->drawArc(rect.adjusted(offset, offset, -offset, -offset), startAngle * 16, spanAngle * 16);

    //Draw major ticks
    painter->translate(width() / 2, height() / 2);
    qreal angleStep = m_SpanAngle / 10;
    for(int i = 0; i < 11; ++i)
        painter->drawLine(rect.width()/2 - 30, 0, rect.width()/2 - 2, 0);
    painter->translate(-width() / 2, -height() / 2);

    //Draw minor ticks
    painter->translate(width() / 2, height() / 2);
    int minorTicks = 8* 10;
    qreal angleStepMin = m_SpanAngle / minorTicks;
    for(int i = 0; i < minorTicks; ++i)
        painter->drawLine(rect.width()/2 - 15, 0, rect.width()/2 - 2, 0);
    painter->translate(-width() / 2, -height() / 2);

    //Draw numbers
    const int radius = rect.width()/2 - 45;
    QFontMetrics fontMat(painter->font());
    QString numbr;
    for (double i = 0; i <= m_SpanAngle; i += m_SpanAngle/10)
        QPoint p( + radius * cos(-90-i * PI / 180.0), - radius * sin(-90-i * PI / 180.0));        numbr = QString::number(i);
        QRect bound = fontMat.boundingRect(numbr);
        painter->drawText(QPointF((p.x() - bound.width()/2), (p.y() + bound.height()/2)), numbr);    

And in the main.cpp we will expose the new item inside the QML.


#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "dialitem.h"

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

    //register new item to the QML
    qmlRegisterType<DialItem>("IVIControls", 1, 0, "DialItem");

    QQmlApplicationEngine engine;
    return app.exec();


And here is my demo usage of my DialItem. Iam using a Slider item to modify the value of the Dial.


import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Controls 1.2
import IVIControls 1.0

ApplicationWindow {
    visible: true
    width: 800
    height: 480

    Rectangle {
        anchors.fill: parent
        color: "#303030"

    DialItem {
        id: speed
        objectName: "speed"
        width: 350
        height: width
        anchors.centerIn: parent
        startAngle: 30
        spanAngle: 300
        dialWidth: 6
        dialColor: "#4646FF"

        Image {
            id: needle
            source: "qrc:/images/needle.png"
            anchors.centerIn: parent
            rotation: 39 + 30 + slider.value
            Behavior on rotation { SpringAnimation { spring: 2; damping: 0.2 } }

    Slider {
        id: slider speed.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        width: 400
        minimumValue: 0
        maximumValue: 300


And finally here is my application running.



Source code:



2 responses to “Custom Dial Item using QQuickPaintedItem”

  1. Sujith SHIVAPRAKASH Avatar

    Hello Arun, is there a way to change the numbering values to a show a different scale, say
    0 – 100 while dividing itself equally and occupying the entire dial ?


    1. Hi Sujith. Sorry for the delay. If I remember correctly I divide the range with the Major tick number.So It should be possible. I couldn’t give it a try.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: