4

I need my QProgressBar to change color on specific values, such as

  • 0-60 Int portion : Green
  • 60-80 Int : Orange
  • 80-100 Int : Red

but I can't find the solution. From style sheet the whole color of progress bar changes on values but not portion wise.

I am using QT creator 3.4.0 based on QT 5.4.1.

IAmInPLS
  • 4,051
  • 4
  • 24
  • 57
Adil Ahmed
  • 43
  • 1
  • 3
  • possible duplicate of [Gradient for chunks in QProgressBar](http://stackoverflow.com/questions/8354345/gradient-for-chunks-in-qprogressbar) – Miki Jul 02 '15 at 10:57

7 Answers7

6

You can do it with stylesheets like this:

QProgressBar* bar = new QProgressBar();
bar->setStyleSheet("::chunk {"
                   "background-color: "
                   "qlineargradient(x0: 0, x2: 1, "
                   "stop: 0 green, stop: 0.6 green, "
                   "stop: 0.60001 orange, stop: 0.8 orange, "
                   "stop: 0.80001 red, stop: 1 red"
                   ")}");

It will look like this:

enter image description here

You can make it smoother simply applying this:

 bar->setStyleSheet("::chunk {"
                    "background-color: "
                    "qlineargradient(x0: 0, x2: 1, "
                    "stop: 0 green, stop: 0.6 green, "
                    "stop: 0.8 orange, "
                    "stop: 1 red"
                    ")}");

enter image description here

Update:

this solution isn't correct, since with values less than maximum you'll get something like this:

enter image description here

As pointed out by others, and here: Gradient for chunks in QProgressBar

the only solution seems to customize the paintEvent

Update2:

Just for completeness, here the relevant code for custom paintEvent. This will work, but it doesn't look very good. Also, most parameters should be class members and set by the user. However:

coloredprogressbar.h

#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

coloredprogressbar.cpp

#include "coloredprogressbar.h"

#include <QPainter>
#include <QBrush>
#include <QStyle>
#include <QPen>
#include <QColor>

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

}

ColoredProgressBar::~ColoredProgressBar()
{

}

void ColoredProgressBar::paintEvent(QPaintEvent*)
{
    int val = value();
    int pos = QStyle::sliderPositionFromValue(minimum(), maximum(), val, width());

    int pos60 = QStyle::sliderPositionFromValue(minimum(), maximum(), 60, width());
    int pos80 = QStyle::sliderPositionFromValue(minimum(), maximum(), 80, width());

    QPainter p(this);
    p.setPen(Qt::green);
    p.setBrush(QBrush(Qt::green));

    if(val >= 0 && val <= 60)
    {
        p.drawRect(0,0,pos,height());
    }
    else if(val > 60 && val <= 80)
    {
        p.drawRect(0,0,pos60,height());
        p.setPen(QColor(255,127,0));
        p.setBrush(QBrush(QColor(255,127,0)));
        p.drawRect(pos60, 0, pos - pos60, height());
    }
    else
    {
        p.drawRect(0,0,pos60,height());
        p.setPen(QColor(255,127,0));
        p.setBrush(QBrush(QColor(255,127,0)));
        p.drawRect(pos60, 0, pos80 - pos60,height());
        p.setPen(Qt::red);
        p.setBrush(QBrush(Qt::red));
        p.drawRect(pos80, 0, pos - pos80, height());
    }

    p.setPen(Qt::lightGray);
    p.setBrush(QBrush(Qt::lightGray));
    p.drawRect(pos, 0, width(), height());


    p.setPen(Qt::black);
    p.setBrush(QBrush(Qt::black));
    p.drawText(0,0, width(), height(), Qt::AlignCenter, QString::number(val) + "%");
}

usage

#include <QApplication>
#include "coloredprogressbar.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    ColoredProgressBar bar;
    bar.setValue(85);
    bar.show();

    return a.exec();
}

I repeat, this is just a simple implementation that doesn't look good and misses some user-settable features, but could be a place to start. Feel free to edit this code.

Community
  • 1
  • 1
Miki
  • 40,887
  • 13
  • 123
  • 202
  • If I `setValue` for ex. to 50, then it would be painted with all colors, when green is only required – westwood Jul 02 '15 at 10:51
  • Your are right, I tried with 100% only. Found also this: http://stackoverflow.com/questions/8354345/gradient-for-chunks-in-qprogressbar that suggests to write a custom paintEvent. Seems the only solution here. – Miki Jul 02 '15 at 10:58
  • The paint event Progress bar is covering whole Widget, how can i customize it for smart vertical Progress bar, also i want more than one progress bars, – Adil Ahmed Jul 03 '15 at 11:04
  • @AdilAhmed 1) you can draw rects not to full `height()`, 2) change the `paintEvent` to draw rect vertically, 3) Use a layout and add the number of progressbar you want. As I already mentioned, this code is just a stub and requires works to fit one's needs, but can be useful to start. – Miki Jul 03 '15 at 11:09
  • @Miki. Please tell me which version of QT you are using? , and which type of application you are selecting on starting up the project, i am confused in Selected the right choice for development (such as C++ App, Widget App, GUI App, Quick Application)? – Adil Ahmed Jul 29 '15 at 14:06
  • @AdilAhmed Qt Widget Application, Qt 5.4.0, msvc2013 32bit, but it will work with any recent Qt and compiler version – Miki Jul 29 '15 at 14:10
  • @Miki. is this code is using the QProgressbar class, or it is an independent paintevent different from Progressbar?, are you writing this code in mainwindow.cpp? – Adil Ahmed Jul 31 '15 at 14:15
  • @AdilAhmed `ColoredProgressBar` inherits from `QProgressBar`, and overloads `paintEvent` – Miki Jul 31 '15 at 14:36
  • @Miki can you please send me the files of this code so i can understand the project tree and inheritance of class from QProgressBar, this will really help me! – Adil Ahmed Aug 01 '15 at 11:47
  • @Miki it is very helpful Thank you :) – Adil Ahmed Aug 04 '15 at 08:56
2

I found a solution that does work with stylesheets, although it's not very flexible. You can set the background of the QProgressBar instead of the chunks and then use an inverted appearance:

QProgressBar.setInvertedAppearance(True)

The above command is for PyQt5, so look op the correct command for c++. Use the following stylesheet, the gradient I use is for a vertical progress bar, so some adjustment is needed to make it horizontally correct. Also use width: instead of height: in that case.

QProgressBar{
    border: 2px solid grey;
    border-radius: 3px;
    text-align: center;
    background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, 
                               stop:0 rgba(0, 170, 0, 255), stop:0.33 rgba(255, 255, 0, 255),
                               stop:0.6 rgba(232, 165, 0, 255), stop:1 rgba(189, 0, 0, 255));
}

QProgressBar::chunk {
    background-color: rgb(50,50,50);
    margin: 0px;
    height: 5px;
}

A limitation of this is that you can not use chunks with margins, and that you have to reverse the values you set for progress. So 100 becomes 0, 70 becomes 30, etc.

You can use this as a continuous progress bar by setting the height of the chunks to a small value like 0.5px. Hope it saves some people the trouble of subclassing QProgressBar and implementing the painter!

Evertvdw
  • 827
  • 10
  • 17
0

You cannot achieve what you want with the existing stylesheet properties. You could however subclass QProgressBar and reimplement the paint in order to get the appearance you wish.

  • Kindly show that how to subclass QProgressBar and re implement the paint in order to get the required appearance, i am using Widget based application (like dragging the objects). – Adil Ahmed Jul 03 '15 at 11:07
0

As @Gabriel de Grimouard already mentioned you need to subclass QProgressBar and reimplement paint. However there is another approach (maybe a bit dirty, but quick) -- subclass QProgressBar and modify setValue slot.

QString style_0_60      = "QProgressBar::chunk{ background-color: green; }";
QString style_60_80     = "QProgressBar::chunk{ background-color: orange; }";
QString style_80_100    = "QProgressBar::chunk{ background-color: red; }";

#include <QProgressBar>

class MyProgressBar : public QProgressBar
{
public:
    MyProgressBar();
    void    setValue(int value);
};

void MyProgressBar::setValue(int value)
{
    if(value >= 0 && value < 60)
        this->setStyleSheet(style_0_60);
    else if (value >= 60 && value < 80)
        this->setStyleSheet(style_60_80);
    else
        this->setStyleSheet(style_80_100);

    QProgressBar::setValue(value);
}
westwood
  • 1,774
  • 15
  • 29
  • This will change the color of the whole background, while OP asked for "portion wise". But this may be useful in some contexts :D – Miki Jul 02 '15 at 10:29
  • You're are right. If mix your's and mine solution then it possible to achieve the result OP is searching (see my comment under your answer) – westwood Jul 02 '15 at 10:51
  • how you declare the class MyProgressBar as a subclass of QProgressBar, i mean in mainwindow.h file or in mainwindow.cpp file? as mainwindow.cpp only permit me to (void Mainwindow::setValue(int value)) but not as (void MyProgressBar::setValue(int value)) – Adil Ahmed Jul 31 '15 at 14:07
  • @Apokal.how you declare the class MyProgressBar as a subclass of QProgressBar, i mean in mainwindow.h file or in mainwindow.cpp file? as mainwindow.cpp only permit me to (void Mainwindow::setValue(int value)) but not as (void MyProgressBar::setValue(int value)) – Adil Ahmed Jul 31 '15 at 14:18
0

I used @Miki example. It helped me a lot but it's in solid colors. To make it gradient, I modified the cpp. As this is a good thread about multiple colors in progress bar, I think this will be a good contribution.

int val = value();
int pos = QStyle::sliderPositionFromValue(minimum(), maximum(), val, width());

QPainter p(this);

QLinearGradient linearGrad(this->rect().topLeft(), this->rect().bottomRight());
linearGrad.setColorAt(0, Qt::red);
linearGrad.setColorAt(0.2, QColor(255, 165, 0));
linearGrad.setColorAt(1, Qt::green);
QRect rect_linear(this->rect().topLeft(), this->rect().bottomRight());
p.fillRect(rect_linear, linearGrad);

p.setPen(Qt::lightGray);
p.setBrush(QBrush(Qt::lightGray));
p.drawRect(pos, 0, width(), height());
debihiga
  • 246
  • 2
  • 10
0

I think can first paint the whole gradient bar. And then just draw the portion we want.

enter image description here

The code, part of code come from @Miki, thank you @Miki

void ColoredProgressBar::paintEvent(QPaintEvent* e) {
    int val = value();
    int w = width();
    int h = height();
    int pos = QStyle::sliderPositionFromValue(minimum(), maximum(), val, width());

    // Paint on pixmap
    QPixmap pixmap(w, h);
    QPainter pixmapPainter(&pixmap);
    QLinearGradient linearGradient(0, 0, w, h);
    linearGradient.setColorAt(0, Qt::green);
    linearGradient.setColorAt(0.5, Qt::yellow);
    linearGradient.setColorAt(1, Qt::red);

    QBrush pixmapBrush(linearGradient);
    pixmapPainter.setBrush(pixmapBrush);
    pixmapPainter.drawRect(0, 0, width(), height());

    // Paint the progress bar
    QPainter painter(this);
    QBrush brush(pixmap);
    painter.setBrush(brush);
    painter.drawRect(0, 0, pos, height());

    // Paint background
    painter.setPen(Qt::lightGray);
    painter.setBrush(QBrush(Qt::lightGray));
    painter.drawRect(pos, 0, width(), height());

    // Paint text
    painter.setPen(Qt::black);
    painter.setBrush(QBrush(Qt::black));
    painter.drawText(0, 0, width(), height(), Qt::AlignCenter, QString::number(val) + "%");
}
Awin
  • 139
  • 2
  • 9
0

There is actually a simple way to achieve the effect shown in the answer of "Awin" with stylesheets only. So you do not need to create a custom progress bar class and override the paintEvent. However, it requires updating the style with setStyle every time you change the progress bar value. Best use Qt's signal/slot system and the valueChanged signal from the progress bar for this.

Create the following function:

auto get_progress_bar_style(double fill_ratio) -> QString
{
    double x1 = 1. - fill_ratio;
    double x2 = x1 + 1.;

    QString style = "QProgressBar::chunk:horizontal{"
                    + "background: QLinearGradient(x1 : " 
                    + QString::number(x1)
                    + ", y1 : 0, x2 : " 
                    + QString::number(x2)
                    + ", y2 : 0, stop : 0 #009900, stop : 0.60 #DDDD00,"
                    + " stop : 0.75 #DDBB00, stop : 1 #DD0000);}";

    return style;
}

The fill_ratio is the current value divided by the maximum value. As one can see, the function only adjusts the x1 and x2 values of the linear gradient with an increasing fill ratio of the progress bar.

As I am fairly new to Qt, I can't tell how "wasteful" this approach is regarding performance. Writing a custom progress bar class as shown in the other answers is probably more robust and performant. However, as a quick solution, the demonstrated approach is sufficient for my use cases.

wychmaster
  • 712
  • 2
  • 8
  • 23