Question

How can one obtain the following effect for text in a QProgressBar from Qt? :

Progress bar text with different colors around the front of the progress bar

The ideea is that I must have a brighter color in the left part of the progress bar.

Was it helpful?

Solution

I would do the custom drawing as follows:

  1. Derive your own progress bar class from QLabel.

  2. Overwrite the paintEvent() function.

  3. In paintEvent(), start drawing with a QPainter:

    • draw the secondary background rectangle
    • draw the text in the secondary color with drawText()
    • draw the first background rectangle (overdrawing first part of text)
    • draw the text in the first color within a rectangle matching the first background rectangle: http://qt-project.org/doc/qt-4.8/qpainter.html#drawText-11

You should end up with what you want to achieve. Due to Qt's default double buffering you should observe no flicker.

OTHER TIPS

With the help of this,

QModernProgressBar.h

#include <QWidget>
#include <QProgressBar>
#include <QPaintEvent>
#include <QStylePainter>
#include <QStyleOption>
#include <QDebug>

class QModernProgressBar : public QProgressBar 
{
    Q_OBJECT
   public:
    explicit QModernProgressBar(QWidget *parent = nullptr);
    ~QModernProgressBar() Q_DECL_OVERRIDE;

   protected:
    void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;

public:
    virtual QString text() const Q_DECL_OVERRIDE;
};

QModernProgressBar.cpp

#include "QModernProgressBar.h"

QModernProgressBar::QModernProgressBar(QWidget *parent) : QProgressBar(parent) {}

QModernProgressBar::~QModernProgressBar() {}

/* https://www.qtcentre.org/threads/70885-QProgressBar-with-in-decimal */

QString QModernProgressBar::text() const
{
    QString result = format();
    if(minimum() ==0 && maximum()==0)
    {
        if(result=="%p%")
        {
            return QString("");
        }
        else
        {
            return result;
        }
    }
    else if ( minimum() == maximum() ) 
    {
        return QString("");
    }

    if(result.isEmpty())
    {
        return QString("%p%");
    }
    else
    {
        return result;
    }
}

void QModernProgressBar::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev)
    QStylePainter paint(this);
    QStyleOptionProgressBar opt;
    initStyleOption(&opt);

    paint.drawControl(QStyle::CE_ProgressBarGroove, opt);
    paint.drawControl(QStyle::CE_ProgressBarContents, opt);

    const QStyleOptionProgressBar *option = &opt;
    QStylePainter *painter = &paint;

    // Stolen from QFusionStyle::drawControl
    if (const QStyleOptionProgressBar *pbar = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
    {
        QRect leftRect;
        QRect rect = pbar->rect;
        QColor textColor            = option->palette.text().color();
        QColor alternateTextColor   = option->palette.window().color();

        painter->save();
        bool vertical = false, inverted = false;
        vertical = (pbar->orientation == Qt::Vertical);
        inverted = pbar->invertedAppearance;
        if (vertical)
            rect = QRect(rect.left(), rect.top(), rect.height(), rect.width());
        const auto totalSteps = qMax(Q_INT64_C(1), qint64(pbar->maximum) - pbar->minimum);
        const auto progressSteps = qint64(pbar->progress) - pbar->minimum;
        const int progressIndicatorPos = static_cast<int>(progressSteps * rect.width() / totalSteps);
        if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width())
            leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height());
        if (vertical)
            leftRect.translate(rect.width() - progressIndicatorPos, 0);

        bool flip = (!vertical && (((pbar->direction == Qt::RightToLeft) && !inverted) ||
                    ((pbar->direction == Qt::LeftToRight) && inverted)));

        QString formattedText = pbar->text;
        formattedText.replace(QLatin1String("%m"), QString("%1").arg(totalSteps));
        const auto progress = static_cast<int>((qint64(value()) - pbar->minimum) * 100.0 / totalSteps);
        formattedText.replace(QLatin1String("%p"), QString("%1").arg(progress));
        QRegion rightRect = rect;
        rightRect = rightRect.subtracted(leftRect);
        painter->setClipRegion(rightRect);
        painter->setPen(flip ? alternateTextColor : textColor);
        painter->drawText(rect, formattedText, QTextOption(Qt::AlignAbsolute |
                                                           Qt::AlignHCenter  |
                                                           Qt::AlignVCenter));
        if (!leftRect.isNull())
        {

            painter->setPen(flip ? textColor : alternateTextColor);
            painter->setClipRect(leftRect);
            painter->drawText(rect, formattedText, QTextOption(Qt::AlignAbsolute |
                                                               Qt::AlignHCenter  |
                                                               Qt::AlignVCenter));
        }
        painter->restore();
    }
}

main.cpp

#include <QApplication>
#include <QTimer>
#include <QTextStream>
#include <QTimer>
#include <QDebug>
#include <QVBoxLayout>
#include "QModernProgressBar.h"

int main(int argc, char* argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QCoreApplication::setApplicationName("QModernProgressbar Demo");
    QApplication app(argc, argv);
    app.setStyleSheet(QLatin1String("                                   \
        QProgressBar {                                                  \
            font-family                 : Fira Code;                    \
            font-size                   : 12pt;                         \
            font-style                  : normal;                       \
            font-weight                 : bold;                         \
            color           : rgba(8, 218, 157, 100%);                  \
            border          : 1px solid rgba(8, 218, 157, 100%);        \
            border-radius       : 0px;                                  \
            text-align          : center;                               \
        }                                                               \
                                                                        \
        QProgressBar::chunk {                                           \
            background-color    : rgba(8, 218, 157, 100%);              \
            width               : 1px;                                  \
            text-align          : center;                               \
        }"));                                                           \

    QTimer timer;
    QWidget mainWindow;
    InvertProgressBar w1, w2, w3;

    w1.setTextVisible(true);
    w2.setTextVisible(true);
    w3.setTextVisible(true);
    int i=25;
    int j=0;

    w1.setRange(0,25);
    w2.setRange(0,100);
    w3.setRange(0,0);

    w1.setFormat(QString("Searching... Timing out in %1 seconds.").arg(i));
    w2.setFormat(QString("Flashing the controller ..."));
    w3.setFormat(QString("Rebooting the controller ..."));
    QVBoxLayout *finalLayout = new QVBoxLayout;

    QObject::connect(&timer, &QTimer::timeout, [&]() {
                                                        w1.setFormat(QString("Searching... Timing out in %1 seconds...").arg(i));
                                                        w1.setValue(i);
                                                        if(--i==-1)
                                                        {
                                                            i=25;
                                                        }
                                                        w2.setFormat(QString("Flashing the controller ...%p%"));
                                                        w2.setValue(j);
                                                        if(j++==101)
                                                        {
                                                            j=0;
                                                        }
                                                      }
                                                      );
    timer.start(500);
    finalLayout->addWidget(&w1);
    finalLayout->addWidget(&w2);
    finalLayout->addWidget(&w3);
    mainWindow.setFixedSize(500,150);
    mainWindow.setLayout(finalLayout);
    mainWindow.show();
    return app.exec();
}

Demo screenshot

Please note that when the busy indicator is on, the dual tone of text is not effective.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top