VSkynet96
VSkynet96

Reputation: 53

Vertical QtProgressBar with rounded edges and dynamic gradient setting not properly shown - C++

I'm trying to build a progressbar like this:

Progress bar with rounded corners and dynamic gradient applied using stylesheets

The problem is that when the value is below a certain percentage, it is shown this way:

Not properly shown progressbar

Now, I know this is a known bug of the application of stylesheets and the only way to escape this is subclassing the QProgressBar class and overriding the paintEvent method. I tried to do this using the suggestions found here progressbar is incorrect displayed but it doesn't work for me. Implementing this in C++ I get a progressbar that is always filled, even if the gradient is applied:

progressbar after paintEvent override

Here I post my QtprogressBar class redefinition

Header file:

#ifndef COLOREDPROGRESSBAR_H
#define COLOREDPROGRESSBAR_H

#include <QWidget>
#include <QProgressBar>
#include <QPaintEvent>

class ColoredProgressBar : public QProgressBar
{
    Q_OBJECT
public:
   explicit ColoredProgressBar(QWidget *parent = 0);
   ~ColoredProgressBar();

protected:
   void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE;

signals:

public slots:
};

#endif // COLOREDPROGRESSBAR_H

Cpp File

#include "ColoredProgressbar.h"

#include <QStyleOptionProgressBar>
#include <QStyle>
#include <QStylePainter>
#include <algorithm>
#include <QWidget>

ColoredProgressBar::ColoredProgressBar(QWidget *parent) : QProgressBar(parent)
{

}

ColoredProgressBar::~ColoredProgressBar()
{

}

void ColoredProgressBar::paintEvent(QPaintEvent*)
{
    QStylePainter painter(this);
    QStyleOptionProgressBar *opt = new QStyleOptionProgressBar();
    this->initStyleOption(opt);
    QRect rect = this->style() >subElementRect(QStyle::SE_ProgressBarContents, opt, this);
    int min_size = rect.width();
    int groove_size = rect.height() - min_size - 1;
    double value_range = this->maximum() - this->minimum();
    double offset = this->value() / value_range * groove_size;
    double new_value = (min_size + 1 + offset) / (rect.height() * value_range);

    if ((int)new_value != new_value)
    {
        new_value = std::min((double)this->maximum(), new_value + 1);
    }

    opt->progress = new_value;
    painter.drawControl(QStyle::CE_ProgressBar, *opt);
}

MainWindow

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ColoredProgressbar.h"
#include "ProgressBar.cpp"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QWidget *progressWidget = new QWidget;
    layout = new QGridLayout(progressWidget);

    progress = new ColoredProgressBar(this);
    progress->setOrientation(Qt::Vertical);
    progress->setRange(0, 100);
    progress->setFixedSize(50, 300);
    progress->setTextVisible(true);
    QString value1 = "QProgressBar {border-radius: 25px};";
    progress->setStyleSheet(value1);
    layout->addWidget(progress,0, 1);

    this->setCentralWidget(progressWidget);

    slider = new QSlider(this);
    slider->setFixedSize(300, 400);
    slider->setMaximum(100);
    slider->setMinimum(0);

   QObject::connect(slider, &QSlider::valueChanged, this, &MainWindow::setProgressValue);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setProgressValue()
{
    progress->setValue(slider->value());
    QString value1 = "QProgressBar {border-radius: 25px} QProgressBar::chunk {background: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,stop: 0 #5ea9d8, stop: 0.30 #439bd1 ,stop: 0.4999 #6eb1dc, stop: 1 #6cb0db ); border-radius: 25px;}";
    QString value2 = "QProgressBar {border-radius: 25px} QProgressBar::chunk {background: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,stop: 0 #5ea9d8, stop: 0.30 #439bd1 ,stop: 0.4999 #6eb1dc, stop: 0.8 #6cb0db ,stop: 1 #F0F150 ); border-radius: 25px;}";
    QString value3 = "QProgressBar {border-radius: 25px} QProgressBar::chunk {background: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,stop: 0 #5ea9d8, stop: 0.30 #439bd1 ,stop: 0.4999 #6eb1dc, stop: 0.6 #6cb0db ,stop: 0.8 #F0F150, stop: 1 #ca3142 ); border-radius: 25px;}";

    if(progress->value()<=50)
    {
        progress->setStyleSheet(value1);
    }
    else if(progress->value()>50 && progress->value()<80)
    {
        progress->setStyleSheet(value2);
    }
    else if(progress->value()>=80)
    {
        progress->setStyleSheet(value3);
    }
}

Upvotes: 2

Views: 895

Answers (1)

SebDieBln
SebDieBln

Reputation: 3469

Note that the Python example you ported to C++ is for a horizontal progressbar whereas you draw a vertical progressbar. Therefore width() and height() need to be swapped.

If you want your class to be able to work for both orientations you can use QProgressBar::orientation() and adapt the calculation based on its result.

Furthermore you want to check your data types. Unlike in C++ in Python every division yields a float value, so take a look at Why does dividing two int not yield the right value when assigned to double? and adapt your code accordingly, i.e. like this:

double value_range = this->maximum() - this->minimum();
double offset = this->value() / value_range * groove_size;
double new_value = (min_size + 1 + offset) / rect.width() * value_range;
if ((int)new_value != new_value)
{
    new_value = std::min(this->maximum(), new_value + 1);
}

Upvotes: 1

Related Questions