3

I'm trying to create the following custom button:

custom button To do this I've create the class and overrided paintEvent:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QPen pen(Qt::darkGray, 7, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    painter.setPen(pen);
    painter.drawEllipse(QPointF(width()/2, height()/2), width()/2.1,height()/2.1);

    QPen pen2(Qt::lightGray, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    painter.setPen(pen2);
    painter.drawEllipse(QPointF(width()/2, height()/2), width()/2.15, height()/2.15);

    QPen pen1(Qt::gray, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    painter.setPen(pen1);
    painter.drawEllipse(QPointF(width()/2, height()/2), width()/3.5, height()/3.5);
}

But, I'm not able to create the button like below with appropriate gradients and picture in the center. Can you please help me?

dtech
  • 47,916
  • 17
  • 112
  • 190
elgolondrino
  • 665
  • 9
  • 33

2 Answers2

3

The key thing here is to make the gradients. And you cannot make gradients on strokes, only on fills. Meaning that you will have to implement the outline as a fill.

There is the button's components digested, listed in the order you should draw them:

enter image description here

Mind you that only the first component is a solid color, everything else is gradients. It is those gradients which get the effect.

In order to get gradients for the inner ring you will have to use QPainterPath, fist add the outer circle, and then add a slightly smaller inner circle, which will effectively punch a hole in the first circle, giving you something that looks like an outline but is actually a fill, a fill you can use a gradient on.

As mentioned in the comments - this involves a lot of operations and is not ideal. It would be preferable to have such buttons as images instead of painting them with QPainter.

Update: here is some more help, to show you how to draw a gradient outline in order to achieve some 3D illusion:

class Test : public QWidget {
    Q_OBJECT
  public:
    Test() { resize(200, 200); }
    void paintEvent(QPaintEvent *) {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.fillRect(rect(), Qt::gray);

        QPainterPath p;
        p.addEllipse(rect().center(), 50, 50);
        p.addEllipse(rect().center(), 45, 45);

        QLinearGradient g(QPointF(0, 50), QPointF(0, 150));
        g.setColorAt(0, Qt::white);
        g.setColorAt(1, Qt::black);

        QBrush b(g);
        painter.fillPath(p, b);

        QPainterPath p2;
        p2.addEllipse(rect().center(), 45, 45);
        p2.addEllipse(rect().center(), 40, 40);

        QLinearGradient g1(QPointF(0, 50), QPointF(0, 150));
        g1.setColorAt(0, Qt::black);
        g1.setColorAt(1, Qt::white);

        QBrush b1(g1);
        painter.fillPath(p2, b1);

        QPainterPath p3;
        p3.addEllipse(rect().center(), 40, 40);
        painter.fillPath(p3, b);
    }
};

And the result:

enter image description here

dtech
  • 47,916
  • 17
  • 112
  • 190
1

Considering the question reworded as:

How to use QPainter to generate the given picture?

Using QPainter:

  1. Get a vectorial image of the button, ideally in SVG.
  2. Look the SVG code to extract colors, gradients, sizes, etc.
  3. Reproduce them with QPainter, you will see that for each SVG command, you have an nearly equal equivalent in QPainter. QPainterPath will help.

Using QPixmap:

  1. Get an SVG image of your button
  2. Use QSvgRenderer to generate a QImage of the given size, and then convert it to a QPixmap.
  3. Paint the QPixmap when required.

Benefits of each options:

  • QPixmap is simpler, and allows you to switch the button aesthetic easily.
  • QSvgRenderer allows you to manage hover easily
  • QPainter allows you to override colors with those of the OS
  • QPainter give you more flexibility for effects, animations, etc.
Adrian Maire
  • 14,354
  • 9
  • 45
  • 85