How to change color of default style for progressbar in Qt

4.5k views Asked by At

How I can change green tint in default QProgressbar style, without changing other default gradients and effects (a little noticeable "flow white chunk" effect):

Default QProgressbar style

.

I was tried to set new combination of background colors for QProgressBar::chunk:horizontal using qlineargradient, but I did not succeed to keep mentioned effect with any of such stylesheets.

2

There are 2 answers

0
Vova Shevchyk On BEST ANSWER

Possibly try to update StyleSheet with timer like this:

mRunner = 0.1;

QTimer *mTimer = new QTimer(this);
connect(mTimer, SIGNAL(timeout()), this, SLOT(updateProgress()));
mTimer->start(40);

and method should change gradient for each new step:

void MainWindow::updateProgress()
{
    QString lStyle = QString("QProgressBar::chunk {background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:%1 white, stop:1 #b4b4b4);}").arg(mRunner);
    ui->progressBar->setStyleSheet(lStyle);

    mRunner += 0.01;
    if (mRunner > 1) {
        mRunner = 0.1;
    }
}
0
Shikyo Kira On

I'm doing the same thing. Keeping the white flowing chunk while changing the chunk color is the difficult part. The white flowing chunk has its own condition as well. Partly using Volodymyr's example, this is what I came up

First we subclass the QProgressBar

ProgressBar.h

class AnimProgressBar : public QProgressBar
{
    Q_OBJECT

private:
    const double c_overtime = 0.25;
    const double c_speed    = 0.025;
    const double c_gsize    = 0.02;
    const double c_gap      = 0.1;
    const double opq        = 180;
    double overtime         = c_overtime;
    double speed            = c_speed;
    double gsize            = c_gsize;
    double gap              = c_gap;
    double aRunner          = -0.2;
    QString hue             = "120";
    QString val             = "170";
    const QString sat       = "255";

    std::mutex pbmtx;
    std::condition_variable pbcv;

    std::atomic<int> trueValue = 0;

    std::atomic_flag setFlag{};

public:
    QString font = "";
    AnimProgressBar(QWidget* parent = nullptr);

private:
    void valueUpdate();
    void commitValue(int value);

public slots:
    void ForeverLoop();
    void newValue(int value);
};

 

ProgressBar.cpp

AnimProgressBar::AnimProgressBar(QWidget * parent)
{
    QTimer *mTimer = new QTimer(this);
    connect(mTimer, SIGNAL(timeout()), this, SLOT(ForeverLoop()));
    mTimer->start(50);
}

Then we set the color changes. In this case I'm using hsv. Each clock cycle 1 + overtime is each cycle for the white flowing chunk. The clock speed is speed. Below are all the scenarios that can happen in the a progress bar flow

void AnimProgressBar::ForeverLoop()
{
    QString color = hue + ", " + sat + ", " + val;
    QString style
        = font + "QProgressBar::chunk {background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, ";
    QString trueopq = QString::number(255 - opq);
    double bRunner = aRunner + gsize;
    double aGap    = aRunner - gap;
    double bGap    = bRunner + gap;

    const auto BigOrEqual = [](double a, double b) { return a > b || AreDoubleSame(a, b); };
    const auto SmallOrEqual = [](double a, double b) { return a < b || AreDoubleSame(a, b); };

    // Symbols below are used to illustrate the progress bar coordination as visual representation
    // [xx] : this is the opaque block
    // [    : left side of the opaque block (aRunner)
    // ]    : right side of the opaque block (bRunner)
    // xx   : size of the opaque block (gap)
    // ---- : this is the solid color section
    // |S|   : this is the start point
    // |E|   : this is the end point
    // <==  : this is the gradient gap

    // |S|----------<==[xx]==>--------|E|
    // Illustration above is visual for progress bar where the opaque block is flowing within the visible section

    // Note: Visible section only exists between start point and end point, meaning in between |S| and |E|

    if (SmallOrEqual(bGap, 0) || BigOrEqual(aGap, 1))
    {
        // <==[xx]==>--|S|---
        // --|E|---<==[xx]==>
        // block and gradient gap are outside of visible section

        style.append("stop:0 hsv(%1)), stop:1 hsv(%1)");
    }
    else if (BigOrEqual(aRunner, 1))
    {
        if (SmallOrEqual(aGap, 0))
        {
            // <=|S|=|E|[xx]
            // whole chunk only contains the gradient gap area

            double num   = 255 - std::fmin(255, std::fabs(aGap) / gap * opq);
            style.append("stop:0 hsv(%1, " + QString::number(num) + ")");
        }
        else
        {
            // --|S|----------<=|E|=[xx]==>
            // small gradient gap still within visible section

            style.append("stop:" + QString::number(1 - gap) + " hsv(%1)");
        }

        double num = 255 - std::fmin(255, (1 + gap - aRunner) / gap * opq);
        style.append(", stop:1 hsv(%1, " + QString::number(num) + ")");
    }
    else if (SmallOrEqual(bRunner, 0))
    {
        // <==[xx]=|S|=>----------
        // only the gradient part of the opaque block is within the visible section at the start

        double num = 255 - std::fmax(0, bGap / gap * opq);
        style.append("stop:0 hsv(%1, " + QString::number(num) + "), stop:" + QString::number(bGap)
                     + " hsv(%1)");
    }
    else if (SmallOrEqual(aRunner, 0))
    {
        // <==[x|S|x]==>
        // only part of the opaque block is within the visible section at the start

        style.append("stop:0 hsv(%1, " + trueopq + ")");

        if (SmallOrEqual(bRunner, 1))
        {
            style.append(", stop:" + QString::number(bRunner) + " hsv(%1, " + trueopq + ")");

            if (BigOrEqual(bGap, 1))
            {
                // <==[x|S|x]==|E|>
                // the progress bar's chunk is small that right end of the gradient gap is hitting the end of the progress bar chunk

                double num   = 255 - std::fmin(255, bGap - 1 / gap * opq);
                style.append(", stop:1 hsv(%1, " + QString::number(num) + ")");
            }
            else
            {
                // <==[x|S|x]==>---------|E|
                //          ^
                //       bRunner
                // the progress bar's chunk gradient gap not hitting the end

                style.append(", stop:" + QString::number(bGap) + " hsv(%1)");
            }
        }
        else
        {
            // <==[x|S|x|E|]==>
            // the progress bar's chunk is so small that fits within the whole block
        }
    }
    else if (SmallOrEqual(aGap, 0))
    {
        // <=|S|=[xx]
        // part of the left gradient is outside of the start point

        double anum = 255 - std::fmin(255, std::fabs(aGap) / gap * opq);
        style.append("stop:0 hsv(%1, " + QString::number(anum) + "), stop:" + QString::number(aRunner) + " hsv(%1, " + trueopq + ")");

        if (SmallOrEqual(bRunner, 1))
        {
            style.append(", stop:" + QString::number(bRunner) + " hsv(%1, " + trueopq + ")");

            if (BigOrEqual(bGap, 1))
            {
                // <=|S|=[xx]=|E|=>
                // the part of the gradient area at left and right are outside of the start point and end point respectively

                double bextra = bGap - 1;
                double num    = 255 - std::fmin(255, gap + bextra / gap * opq);
                style.append(", stop:1 hsv(%1, " + QString::number(num) + ")");
            }
            else
            {
                // <=|S|=[xx]==>-------|E|
                // part of the left gradient is outside of the start point

                style.append(", stop:" + QString::number(bGap) + " hsv(%1)");
            }
        }
        else
        {
            // <=|S|=[x|E|x]==>
            // the part of the gradient area at left is outside of the start point while the block is outside of the end point
        }
    }
    else if (BigOrEqual(bRunner, 1))
    {
        // ------<==[x|E|x]==>
        // only part of the opaque block is within the end point

        style.append("stop:" + QString::number(aGap) + " hsv(%1), stop:" + QString::number(aRunner) + " hsv(%1, "
                     + trueopq + "), stop:0.99995 hsv(%1, " + trueopq + "), stop:1 hsv(%1)");
    }
    else if (BigOrEqual(bGap, 1))
    {
        // ------<==[xx]=|E|=>
        // part of the right gradient is outside of the end point

        double bextra   = bGap - 1;
        double bnum     = 255 - std::fmin(255, gap + bextra / gap * opq);
        style.append("stop:" + QString::number(aGap) + " hsv(%1), stop:" + QString::number(aRunner)
                     + " hsv(%1, " + trueopq + "), stop:" + QString::number(bRunner) + " hsv(%1, " + trueopq
                     + "), stop:1 hsv(%1, " + QString::number(bnum) + ")");
    }
    else
    {
        // |S|----------<==[xx]==>--------|E|
        // the opaque block is flowing within the visible section

        style.append("stop:" + QString::number(aGap) + " hsv(%1), stop:" + QString::number(aRunner)
                     + " hsv(%1, " + trueopq + "), stop:" + QString::number(bRunner) + " hsv(%1, " + trueopq
                     + "), stop:" + QString::number(bGap) + " hsv(%1)");
    }

    style.append(");}");

    setStyleSheet(style.arg(color));

    aRunner += speed;

    if (aRunner < 0 - overtime || aGap > 1 + overtime) aRunner = 0 - overtime;
}

For every change in the value, we modify the hsv, gap and the clock speed

void AnimProgressBar::newValue(int value)
{
    int max = maximum();

    if (0 < value && value <= max)
    {
        double decivalue = value;
        double power     = decivalue / max;
        double mult      = max / std::min(max, value);
        overtime         = c_overtime * mult;
        speed            = c_spped * mult;
        gsize            = c_gsize * mult;
        gap              = c_gap * mult;
        hue              = QString::number(static_cast<int>(120 - (decivalue / max * 120)));
        val              = QString::number(static_cast<int>(170 + (decivalue / max* 55)));
    }

    if (value == max * 7 / 10) font = "QProgressBar { color: rgb(255, 255, 255); } ";

    setValue(value);
}

EDIT: Rewrote the update loop