6.3. Specialize an Existing Widget

Specialization of an existing widget is the easiest way to create new widgets. It is achieve by declaring a class that inherits from an existing functional widget that is not a QWidget_.

In this case, the presentation of the widget satisfies the requirements, but some manipulation of an input property is needed.

We will explore an existing widget, the QeDmsLabel_ to explain more on this topic.

6.3.1. Declaration

This is the class declaration of the QeDmsLabel, a widget that is part of CUT. This widget inherits from QLabel. We only need to present text, but we want to make the transformations needed to present the value part of the widget.

 1class QeDmsLabel : public QLabel {
 2    Q_OBJECT
 3
 4    Q_PROPERTY(double radians MEMBER m_radians READ getRadians WRITE setRadians NOTIFY radiansChanged )
 5    Q_PROPERTY(int precision MEMBER m_precision READ getPrecision WRITE setPrecision NOTIFY precisionChanged )
 6
 7public:
 8    explicit QeDmsLabel(QWidget *parent = Q_NULLPTR);
 9    ~QeDmsLabel();
10    double getRadians(){ return m_radians; };
11    int getPrecision(){ return m_precision; };
12
13signals:
14    void radiansChanged(double newValue);
15    void precisionChanged(int newValue);
16
17public slots:
18    void setRadians(double newValue){this->m_radians = newValue; emit radiansChanged(newValue); this->update();};
19    void setPrecision(int newValue){this->m_precision = newValue; emit precisionChanged(newValue); this->update();};
20    void update();
21
22private:
23    double m_radians = 0.0;
24    int m_precision = 3;
25};
  • In line 1, the widget is declared to inherit from QLabel_. This in turn inherits from QWidget_. As always, any widget must inherit at some point from QWidget_.

  • Lines 4 and 5 add two properties: radians and precision.

  • getter method of the properties are usually public.

  • Notice that in lines 14 and 15, both properties have signals as declared after the NOTIFY section of the Q_PROPERTY macro.

  • setter methods are declared under the public slots qualifier (line 17). It is a common practice to do so, as it allows the widget to be connected to another signal.

The two new properties are:

Radians

This radians property will act as the new input property, instead of text.

Precision

precision is a configuration property, and modifies the way the widget behaves.

Note

Though in this document radians and precision are called input and configuration properties respectively, there is no distinction between them expect how we use them in implementation.

6.3.2. Implementation

The implementation of the methods is rather simple. Please keep in mind that signals need no implementation:

 1QeDmsLabel::QeDmsLabel(QWidget *parent) :
 2  QLabel(parent)
 3{
 4
 5}
 6
 7QeDmsLabel::~QeDmsLabel()
 8{
 9}
10
11void QeDmsLabel::setRadians(double newValue){
12    if( newValue == this->m_radians ){
13        return;
14    }
15    this->m_radians = newValue;
16    emit radiansChanged(newValue);
17    this->update();
18}
19
20void QeDmsLabel::setPrecision(int newValue){
21    if( newValue == this->m_precision ){
22        return;
23    }
24    this->m_precision = newValue;
25    emit precisionChanged(newValue);
26    this->update();
27}
28
29void QeDmsLabel::update()
30{
31    int values[4];
32    char sign;
33    eraA2af(this->m_precision, this->m_radians, &sign, values );
34    this->setText(QString("%1%2:%3:%4.%5")
35                    .arg(QChar(sign))
36                    .arg(values[0], 3, 10, QChar('0'))
37                    .arg(values[1], 2, 10, QChar('0'))
38                    .arg(values[2], 2, 10, QChar('0'))
39                    .arg(values[3], this->m_precision, 10, QChar('0')));
40    QLabel::update();
41}
  • The constructor at line 1 calls its ancestors constructor. It is important that it accepts the parent argument in the widget constructor, as many properties are passed from parent to child during UI setup.

  • The setter methods in line 11 and 20 check first if the value is the same, in which case it returns immediately. The widget only acts when there is change.

  • Lines 16 and 25 uses the emit keyword, that is used to emit a signal. In this case, the corresponding signal for the property. The argument is the new value for the property.

  • Lines 17 and 26 shows how the setter, at the end, call the update() method. Since a property essential for presentation has changed, we queue indicate to Qt that a refresh or update of the presentation is needed.

  • Lines 29 to 39 shows the implementation of the update method. Essential is call an external library to perform a radians to Degrees Minutes Seconds representation, with a defined precision. It sets the text property to that string.

  • Notice how in line 40, the ancestors update() method is invoked. At this point, we have called no drawing instruction, no custom QPainter_ methods. Since the only alteration this implementation does to a QLabel_ is to prepare a custom string of text, we only need to invoke the QLabel::update() method and it will render it for us.

This that, the implementation is done.