JokerMartini
JokerMartini

Reputation: 6147

How to add property to custom control in Qt C++?

How do I properly add a property to a custom control, which when changed triggers the paint event? The property only accepts an integer value of 0, 1 or 2.

The setter of the property would be called setPattern, so an example use would be setPattern(0).

This property value will eventually be used to control the pattern drawn in the paint event.

labelheader.h

#ifndef LABELHEADER_H
#define LABELHEADER_H

#include <QLabel>

class LabelHeader : public QLabel
{
    Q_OBJECT
public:
    explicit LabelHeader(QWidget *parent = nullptr);
    explicit LabelHeader(const QString &text, QWidget *parent = nullptr);

signals:

public slots:

    // QWidget interface
protected:
    void paintEvent(QPaintEvent *event);
};

#endif // LABELHEADER_H

labelheader.cpp

#include "labelheader.h"
#include <QPainter>

LabelHeader::LabelHeader(QWidget *parent) :
    QLabel(parent)
{

}

LabelHeader::LabelHeader(const QString &text, QWidget *parent) :
    QLabel(text, parent)
{

}

void LabelHeader::paintEvent(QPaintEvent *event)
{
    // calculate font width
    QFontMetrics metrics(font());
    int text_width = metrics.boundingRect(text()).width();

    // calculate dimensions
    int y = height() * 0.5;
    int x = text_width + 4;

    // create pattern
    QPixmap px(4, 4);
    px.fill(Qt::transparent);

    QPainter pattern_painter(&px);

    // Create ashed 3 dots
    pattern_painter.setPen(Qt::NoPen);
    pattern_painter.setBrush(QBrush(palette().color(QPalette::WindowText), Qt::SolidPattern));
    pattern_painter.drawRect(0, 0, 1, 1);
    pattern_painter.drawRect(2, 2, 1, 1);

    // draw
    QPainter painter(this);

    // Draw dashed 3 dots
    painter.drawTiledPixmap(x, y-2, width()-x, 5, px);

    // Draw solid line
    //painter.drawLine(x,y,width(),y);

    QLabel::paintEvent(event);
}

Upvotes: 2

Views: 1383

Answers (3)

scopchanov
scopchanov

Reputation: 8399

Solution

To accomplish what you need you do not actually need QProperty, but a private attribute of your LabelHeader class. Here is a step-by-step guide, covering all your requirements:

  1. Create the enumeration type

In LabelHeader.h add:

enum PatternType {
    PatternOne = 0,
    PatternTwo,
    PatternThree
};
  1. Declare a private attribute to hold the current pattern

In LabelHeader.h add:

private:
    PatternType m_pattern;
  1. Right click m_pattern and select Refactor->Create Getter and Setter Member Functions
  2. In the implementation of the setter invoke a paintEvent by calling update(), but only if the new pattern is different than the current one:

    if (m_pattern == pattern)
        return;
    
    m_pattern = pattern;
    update();
    
  3. In the constructor add m_pattern to the initializer list to set its value by default

  4. Use the value in the LabelHeader::paintEvent to draw the corresponding pattern

    // draw tiled pixmap
    QPainter painter(this);
    
    switch (m_pattern) {
    case PatternOne:
        painter.drawTiledPixmap(x, y-2, w, 5, px);
        break;
    case PatternTwo:
        painter.drawTiledPixmap(x, y, w, 5, px);
        break;
    default:
        painter.drawTiledPixmap(x, y+2, w, 5, px);
    }
    

Example

I have extended the example from this answer to cover your current requirements as well. The full code is available on GitHub.

Remarks

Regarding the use of Q_PROPERTY, here is an important explanation kindly provided by @KubaOber:

Q_PROPERTY is a Qt idiom used to both convey a meaning to a human reader, and expose the property as such to the metatype system, making it easily scriptable from QML, etc.

Upvotes: 2

Marker
Marker

Reputation: 977

This is how I do it. NOTE: I haven't written an actual custom control in a "library", I usually just use "promoted widgets".

Header File:

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QObject>
#include <QWidget>

class MyWidget: public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int value READ value WRITE setValue)

    private:
        int m_value;

    public:
        MyWidget(QWidget *parent=0);

        inline int value() const {return m_value;}
        void setValue(int value);

    signals:
        void valueChanged();
};

#endif // MYWIDGET_H

Source File:

#include "MyWidget.h"

MyWidget::MyWidget(QWidget *parent)
    :QWidget(parent)
{
    m_value = 0;
}

void MyWidget::setValue(int value)
{
    if (m_value == value) return;

    m_value = value;
    emit valueChanged();

    update();   // This will cause repaint
}

Upvotes: 2

Jaa-c
Jaa-c

Reputation: 5137

You can simply call update() in the setter. If you want to only accept limited range of values, you can limit that in a setter or use an enum instead of an int.

#include <QLabel>

class LabelHeader : public QLabel
{
    Q_OBJECT
public:
    Q_PROPERTY(Pattern pattern READ(getPattern) WRITE(setPattern))

    enum Pattern { FIRST = 0, SECOND, THIRD };
    Q_ENUM(Pattern)

...

    void setPattern(Pattern value)
    {
        if (pattern != value)
        {
            pattern = value;
            update();
        }
    }

    Pattern getPattern() const { return pattern; }
...

private:
    Pattern pattern { FIRST };
};

Upvotes: 3

Related Questions