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
andprecision
.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 theQ_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 oftext
.- 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 theQLabel::update()
method and it will render it for us.
This that, the implementation is done.