Trevir
Trevir

Reputation: 1313

Change color/text of QProgressBar on mouse over filled bar

How is it possible to change the color and/or text(format) of a QProgressBar if the mouse is over the filled part of the bar?

I have stacked multiple QProgressBars on top of each other, each showing more content than the previous.

I want to highlight the biggest bar still under the mouse on mouseover and show some bar-specific text. However, the bars are the same size and therefore I want to recognize the filled area of the bar.

image

E.g., if the mouse is over the third bar, I want to highlight the part from the left to the third bar and show text specific to the third bar.

This is the code I use for the stacked ProgressBars:

class MultiProgressBar : public QWidget
{
    Q_OBJECT
public:
    MultiProgressBar( QWidget* parent = Q_NULLPTR ) : QWidget( parent ), layout( new QStackedLayout( this ) )
    {
        layout->setMargin( 0 );
        layout->setStackingMode( QStackedLayout::StackAll );
    }

    void insertBar( QColor const& color )
    {
        auto bar = new QProgressBar();
        bar->setTextVisible( false );
        bar->setRange( 0, 10000 );

        QPalette palette = this->palette();
        palette.setColor( QPalette::Highlight, QColor( color.red(), color.green(), color.blue(), 100 ) );
        palette.setColor( QPalette::Base, QColor( color.red(), color.green(), color.blue(), 0 ) );
        bar->setPalette( palette );

        layout->addWidget( bar );
        progress_bars.push_back( bar );
    }

public slots:
    void setValues( const std::vector< int >& values, const std::vector< std::string >& names )
    {
        if ( values.size() < progress_bars.size() )
        {
            for ( auto* widget : progress_bars )
            {
                layout->removeWidget( widget );
            }
            progress_bars.clear();
        }
        while ( progress_bars.size() < values.size() )
        {
            insertBar( QColor( 0x46, 0xA1, 0xD9, 255 ) );
        }

        for ( auto i = 0; i < progress_bars.size(); ++i )
        {
            progress_bars[ i ]->setValue( values[ i ] );
            // progress_bars[ i ]->setFormat( QString::fromStdString( names[ i ] ) );
        }
    }

private:
    std::vector< QProgressBar* > progress_bars;
    QStackedLayout* layout;
};

Upvotes: 0

Views: 623

Answers (1)

J. Craqueador
J. Craqueador

Reputation: 101

1) To change color of filled area you can set new color to the palette of progressbar like this:

QPalette newPalette = bar.palette();
newPalette.setColor(QPalette::Highlight, "red"); // setting color to red
bar.setPalette(newPalette);

2.1) Assign text to progressbar you can with void setFormat(const QString &format); function like in your commented string

progress_bars[ i ]->setFormat( QString::fromStdString( names[ i ] ) );

2.2) Get this text you can by calling QProgressBar::text() function;

3) If you want to highlight specific progressbars you can reimplement

QWidget::mouseMoveEvent(QMouseEvent *event);

in which you can calculate margins of filled area:

void mouseMoveEvent(QMouseEvent *e){
    highlightProgressBars(e->pos()); // passing position of mouse cursor
}

NOTE: Don't forget to enable mouse tracking for progressbars and your MultiProgressBar widget:

bar->setMouseTracking(true);

4) Function below gets position of mouse in parameter. It highlights area from left to pointed progressbar inclusively and shows in console text assigned to highlighted progressbars through format property

void highlightProgressBars(QPoint point){
    int widthOfBar = ((QProgressBar*)progress_bars.at(0))->width();
    int valueForPoint = 10000 / widthOfBar; // value of progressbar for width==1
    for (auto pb = this->progress_bars.begin(); pb != this->progress_bars.end(); ++pb) { // iterating vector to paint progressbars
        int leftMargin = 0; // "left margin" of current progressbar is in fact right margin of filled area of previous progressbar
        if(pb != this->progress_bars.begin()){ // except first progressbar in vector which doesn't have previous progressbar
            --pb; // get previous progressbar
            leftMargin = ((QProgressBar*)*pb)->value() / valueForPoint; // get width of filled area
            ++pb; // return to current progressbar
        }

        if(leftMargin < point.x()) { // if position of cursor is to the right of "left margin" of current progressbar we highlight it
            QPalette newPal = ((QProgressBar*)*pb)->palette();  //getting palette
            newPal.setColor(QPalette::Highlight,"red"); // and setting color of filled area
            ((QProgressBar*)*pb)->setPalette(newPal); // finally setting palette to widget
            qDebug() << ((QProgressBar*)*pb)->text(); // show text of highlighted progressbar in console
        }
        else{ // if not we highlight it another way
            QPalette newPal = ((QProgressBar*)*pb)->palette();
            newPal.setColor(QPalette::Highlight,"lightblue");
            ((QProgressBar*)*pb)->setPalette(newPal);
        }

    }
}

If you want to highlight only one progressbar you should change in code above this:

if(leftMargin < point.x()) {

to:

int rightMargin = ((QProgressBar*)*pb)->value() / valueForPoint;
if(leftMargin < point.x() && point.x() < rightMargin) {

Upvotes: 2

Related Questions