Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

stylesheet "indeterminate" progress bar with gradient?



  • I have a progress bar that looks just how i want it, except for the "indeterminate" state, which i think should have a gradient.

    Here's the style i currently, saying nothing about the "indeterminate" look:

    QString			styleStr;
    SuperString		accent_saturated(RGBConvertFtoHexStr(GetOSAccentColor()) + ";");
    SuperString		accent_light(GetHexAccentColorStr() + ";");
    
    styleStr += "QProgressBar { ";
    styleStr += "height: 16px;";
    styleStr += "border-style: solid;";
    styleStr += "border-width: 2px;";
    styleStr += "border-radius: 3px;";
    styleStr += "border-color: ";
    styleStr += accent_saturated;
    styleStr += "}";
    
    styleStr += "QProgressBar::chunk {";
    styleStr += "width: 1px;";
    styleStr += "background-color: ";
    styleStr += accent_light;
    styleStr += "}";
    
    barP->setTextVisible(false);
    barP->setStyleSheet(styleStr);
    

    Looks like this:

    regular.png
    but the INDETERMINATE state looks way too similar, with hard edges:

    bad.png
    what i WANT is for indeterminate to have a gradient, i want it to look like this: (this mockup with photoshop):

    good.png
    Also, animating, of course, as it is doing now.... Does anyone know how to do that?

    -dave



  • If the stylesheet is applied to "indeterminate" progress bars only, then it is quite easy.
    Just set the ::chunk's background-color to a Gradient property and don't set the width of it, something like

    QProgressBar::chunk{
        background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0.5, y2:0, stop:0 white, stop:1 orange);
    }
    

    But if it will apply to both kinds of states, then I don't know yet, since the :indeterminate pseudo-state seems not working for QProgressBar.



  • i want to set the style on the progress bar and have it look correct regardless of whether it is put into an indeterminate state. ie: set up the style at the start of the app, then while it is running i can switch between regular and indeterminate at will without having to fiddle with the sheet DURING the run of the app.

    also: does your solution ANIMATE the gradient across the bar, the same way the default indeterminate animation goes?

    are you saying there's an implementation bug in Qt for this use-case?



  • i tried to do this:

    styleStr += "QProgressBar::chunk {";
    styleStr += "width: 1px;";
    styleStr += "background-color: ";
    styleStr += accent_light;
    styleStr += "}";
    
    styleStr += "QProgressBar::chunk:indeterminate {";
    styleStr += "width: 1px;";
    styleStr += "background-color: ";
    styleStr += "qlineargradient(spread:reflect, x1:0, y1:0, x2:0.5, y2:0, stop:0 white, stop:1 ";
    styleStr += accent_light;
    styleStr += ");}";
    

    but the indeterminate progress bar did NOT change, looks exactly the same as before. did i do this wrong? how?



  • my "solution" is to do this DURING a paint event:

    void	CProgressBar::updateStyle(bool forceB)
    {
    	bool		is_determinateB(isDeterminate());
    
    	if (forceB || is_determinateB != i_determinateB) {
    		i_determinateB = is_determinateB;
    
    		QString			styleStr;
    		SuperString		accent_saturated(RGBConvertFtoHexStr(GetOSAccentColor()));
    		SuperString		accent_light(GetHexAccentColorStr());
    
    		styleStr += "QProgressBar { ";
    		styleStr += "height: 14px;";
    		styleStr += "padding:1px;";
    		styleStr += "border-style: solid;";
    		styleStr += "border-width: 1px;";
    		styleStr += "border-radius: 3px;";
    		styleStr += "border-color: ";
    		styleStr += accent_saturated;
    		styleStr += "}";
    
    		styleStr += "QProgressBar::chunk {";
    		styleStr += "background: ";
    
    		if (i_determinateB) {
    			styleStr += accent_light;
    			styleStr += ";";
    		} else {
    			styleStr += "qlineargradient(x1:0, y1:0, x2:1, y2:0, ";
    			styleStr += "stop: 0 white, stop:0.5 ";
    			styleStr += accent_saturated;
    			styleStr += ", stop: 1 white";
    			styleStr += ");";
    			styleStr += "width: 150px;";
    		}
    
    		styleStr += "margin: 0.5px}";
    
    		setStyleSheet(styleStr);
    	}
    }
    

    i don't think i should be doing it during a paint event, but it seems that's the only choice i have given that CProgressBar doesn't seem to have marked any of its functions as virtual. is there a better way?



  • I can think of a slightly better solution: property selector, since "indeterminate" of QProgressBar means minimum=0 and maximum=0.

    QProgressBar[minimum="0"][maximum="0"]::chunk{
    ...
    }
    

    But it has limitations, according to the documentation of Property Selector:

    Warning: If the value of the Qt property changes after the style sheet has been set, it might be necessary to force a style sheet recomputation. One way to achieve this is to unset the style sheet and set it again.

    As I tested, actually you don't need to unset it and set it again, just set it to the current value.
    So when switching between "indeterminate" state and regular state, after setting the maximum and minimum value, you need to call

    ui->progressBar->setStyleSheet(ui->progressBar->styleSheet());
    

    This even works when the progress bar has only empty stylesheet and the QProgressBar stylesheet is set on its parent.



  • If it were possible to override setrange, I wouldn’t have to do this during a paint event. But the fact remains that I cannot override setrange, which means I cannot implement the above suggestion. Ie: This has to work on all progress bars, even ones that I do not I have direct access to, for example in the QProgressDialog



  • @davecotter
    I don't understand, why do you need to override setRange?

    If you won't change the range, then its properties won't change and this can totally work.
    The limitation only exists when the properties change.
    So if you start a QProgressDialog with minimum=maximum=0 and never change that, then it can work.

    But even if you need to change the minimum / maximum of the QProgressDialog , it can still work by resetting the stylesheet on the QProgressDialog / any parent / grandparent / etc of the progress bar.



  • Which is exactly why I need it, and why I would need to override the “setrange“ call. My progress bars go from indeterminate to determinate, and in fact on start up they can go from determine it back to indeterminate for a time, so it must accommodate the parameters changing. The only solution I found is to do it during the paint event, which I don’t like. I just wish some of the methods were marked virtual. to be clear: it's the scenario where i can't change the client code (users of the progress bar), the only code i can change or have access to is the subclass of QProgressBar.


Log in to reply