Qt - not all widgets refreshed after stylesheet change - qt

I have style manager, so user can change stylesheet and palette in runtime. But after this operation, not all widgets are "restyled". New look is applied when widget is recreated. Same code called from main window constructor is working like expected. Is exists any global signal which notify all widgets that they should refresh palette and stylesheet?
QFile file(":/qss/default.qss");
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet);
QPalette p = qApp->palette();
p.setColor(QPalette::Window, QColor(53,53,53));
p.setColor(QPalette::Button, QColor(53,53,53));
p.setColor(QPalette::Highlight, QColor(175,0,0));
p.setColor(QPalette::ButtonText, QColor(255,255,255));
p.setColor(QPalette::WindowText, QColor(255,255,255));
qApp->setPalette(p);

Related

Qt set default value for style-sheet property

I have an application where users can modify the Qt stylesheets. I want to set default value for some property in case the user-provided stylesheet does not define them, e.g.
// user provided style sheet for the whole app
qApp->setStyleSheet("file://user-provided.qss");
// default value for my button
myButton->setStyleSheet("color: red"); // I'd like this to apply only if the stylesheet does not provide it
The way Qt cascading works, setting the stylesheet on the widget override anything from the application style-sheet, even if the specifier on the application stylesheet are stronger.
I tried using QPalette, or different foreground roles with custom color in the .ui, but nothing worked. Unless I use setStyleSheet, the foreground color does not change (as expected since QPalette is ignored when a stylesheet is used).
Is there a way to have a default value in case a property is missing from the application stylesheet? Basically reverse the cascading effect of Qt?
You can set the "default" stylesheet with all needed style rules on the QApplication instance (for the whole application) and the "user" stylesheet on the top level widget.
So the stylesheet set on the top level widgets extends and overrides the application stylesheet.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget;
auto label_1 = new QLabel(&widget);
label_1->setObjectName(QString::fromUtf8("label_1"));
label_1->setText("Label 1");
auto label_2 = new QLabel(&widget);
label_2->setObjectName(QString::fromUtf8("label_2"));
label_2->setText("Label 2");
auto layout = new QVBoxLayout(&widget);
layout->addWidget(label_1);
layout->addWidget(label_2);
widget.setLayout(layout);
QString defaultStyle = "#label_1{color:red};"; // default for label_1
QString userStyle = "#label_2{color:blue};"; // rule for label_1 is missing
app.setStyleSheet(defaultStyle); // set the default stylesheet on the application
widget.setStyleSheet(userStyle); // set the user stylesheet on the top level widget
widget.show(); // label 1 has blue color and label 2 has red color
return app.exec();
}

How to set animated icon to QPushButton in Qt5?

QPushButton can have icon, but I need to set animated icon to it. How to do this?
I created new class implemented from QPushButton but how to replace icon from QIcon to QMovie?
This can be accomplished without subclassing QPushButton by simply using the signal / slot mechanism of Qt. Connect the frameChanged signal of QMovie to a custom slot in the class that contains this QPushButton. This function will apply the current frame of the QMovie as the icon of the QPushButton. It should look something like this:
// member function that catches the frameChanged signal of the QMovie
void MyWidget::setButtonIcon(int frame)
{
myPushButton->setIcon(QIcon(myMovie->currentPixmap()));
}
And when allocating your QMovie and QPushButton members ...
myPushButton = new QPushButton();
myMovie = new QMovie("someAnimation.gif");
connect(myMovie,SIGNAL(frameChanged(int)),this,SLOT(setButtonIcon(int)));
// if movie doesn't loop forever, force it to.
if (myMovie->loopCount() != -1)
connect(myMovie,SIGNAL(finished()),myMovie,SLOT(start()));
myMovie->start();
Since I had to solve this problem for a project of mine today, I just wanted to drop the solution I found for future people, because this question has lots of views and I considered the solution quite elegant. The solution was posted here. It sets the icon of the pushButton every time, the frame of the QMovie changes:
auto movie = new QMovie(this);
movie->setFileName(":/sample.gif");
connect(movie, &QMovie::frameChanged, [=]{
pushButton->setIcon(movie->currentPixmap());
});
movie->start();
This also has the advantage, that the icon will not appear, until the QMovie was started. Here is also the python solution, I derived for my project:
#'hide' the icon on the pushButton
pushButton.setIcon(QIcon())
animated_spinner = QtGui.QMovie(":/icons/images/loader.gif")
animated_spinner.frameChanged.connect(updateSpinnerAniamation)
def updateSpinnerAniamation(self):
#'hide' the text of the button
pushButton.setText("")
pushButton.setIcon(QtGui.QIcon(animated_spinner.currentPixmap()))
Once you want to show the spinner, just start the QMovie:
animated_spinner.start()
If the spinner should disappear again, then stop the animation and 'hide' the spinner again. Once the animation is stopped, the frameChanged slot won't update the button anymore.
animated_spinner.stop()
pushButton.setIcon(QtGui.QIcon())

Qt StyleSheet custom style attribute custom QGLwidget

I have created a subclass of QGLwidget and I was hoping that I could use a stylesheet to tell openGL how to render a scene.
For Example:
qApp->setStyleSheet("CustomWidget { background-color: yellow }");
Then in my paintGL method:
QColor bg = "Get 'background-color' style somehow"
glClearColorf(bg.redF(), bg.greenF(), bg.blueF(), 0);
glClear(GL_COLOR_BUFFER_BIT)
Also, is it possible to create custom style sheet attributes?
qApp->setStyleSheet("CustomWidget { foo-attr: 1 }");
I have read up on the QStyle and QStyleOption classes, but I don't quite understand how to apply them to a practical application.
You can declare Q_PROPERTY in your custom widget and then set them with
CustomWidget
{
qproperty-yourPropertyName: "value";
}
You can access BG of your custom widget with QPalette
QColor bg = palette().color(QPalette::Window);
But I'm not sure if it will work

QT: setStyleSheet from a resource QSS file?

In my widget, I can do something like that:
MyWindow::MyWindow(QWidget *parent) :
QWidget(parent)
{
ui.setupUi(this);
setStyleSheet("QWidget { background-color: red }"); // <--- HERE
}
This will set the widget background red.
I have a QSS file in my resources. How do I instruct my widget to take its style sheet content from there, vs just taking the qss syntax as parameter?
As an alternative to setting a style sheet for each widget, you can just load and set a stylesheet for a whole application. Something like this:
QApplication app( argc, argv );
// Load an application style
QFile styleFile( ":/style.qss" );
styleFile.open( QFile::ReadOnly );
// Apply the loaded stylesheet
QString style( styleFile.readAll() );
app.setStyleSheet( style );
In this case all widgets will pick their styles from the given stylesheet automatically.
Got it: you actually have to "read the file" from the resources, convert it to a QString and feed it to the setStyleSheet. E.g.:
QFile file(":/qss/default.qss");
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
setStyleSheet(styleSheet);

Resetting Qt Style Sheet

I've managed to style my QLineEdit to something like this:
alt text http://www.kimag.es/share/54278758.png
void Utilities::setFormErrorStyle(QLineEdit *lineEdit)
{
lineEdit->setStyleSheet(
"background-color: #FF8A8A;"
"background-image: url(:/resources/warning.png);"
"background-position: right center;"
"background-repeat: no-repeat;"
"");
}
I called the function using
Utilities *util = new Utilities;
util->setFormErrorStyle(lineNoStaf);
The flow should be something like this:
User open form
User fill data
User submit data
Got error
Use setFormErrorStyle()
User edit the text in the QLineEdit and the style disappear
This function should be reusable over and over again, but how can I connect QLineEdit signal such as textChanged() to a function in other class that will reset the Style Sheet and then disconnect the signal so that it won't be running continuously every time the text changed ?
Qt also allows dynamic properties in its stylesheet, that means you don't need to code your own class for every widget type in your form.
From http://qt-project.org/doc/qt-4.8/stylesheet-examples.html
Customizing Using Dynamic Properties
There are many situations where we need to present a form that has mandatory fields. To indicate to the user that the field is mandatory, one effective (albeit esthetically dubious) solution is to use yellow as the background color for those fields. It turns out this is very easy to implement using Qt Style Sheets. First, we would use the following application-wide style sheet:
*[mandatoryField="true"] { background-color: yellow }
This means that every widget whose mandatoryField Qt property is set to true would have a yellow background.
Then, for each mandatory field widget, we would simply create a mandatoryField property on the fly and set it to true. For example:
QLineEdit *nameEdit = new QLineEdit(this);
nameEdit->setProperty("mandatoryField", true);
QLineEdit *emailEdit = new QLineEdit(this);
emailEdit->setProperty("mandatoryField", true);
QSpinBox *ageSpinBox = new QSpinBox(this);
ageSpinBox->setProperty("mandatoryField", true);
Works also in Qt 4.3!
Allright, this is not compile but should work in principle, you should be able to change the look by calling editWidget->setProperty('isError',true) or editWidget->setError(false)
class ErrorTextEdit : QLineEdit
{
Q_OBJECT
QPROPERTY(bool isError, READ isError, WRITE setError);
public:
ErrorTextEdit(QWidget* parent) : QLineEdit(parent), m_isError(false)
{
m_styleSheet = "" // see below
setStyleSheet(m_styleSheet);
}
void setError(bool val)
{
if (val != m_isError)
{
m_isError = val;
setStyleSheet(m_styleSheet);
}
}
bool isError() {return m_isError;}
private:
QString m_styleSheet;
bool m_isError;
}
for the stylesheet
ErrorTextEdit[isError="false"]
{
optional ...
Style for textedit that is NOT an error
}
ErrorTextEdit[isError="true"]
{
background-color: #FF8A8A;
background-image: url(:/resources/warning.png);
background-position: right center;
background-repeat: no-repeat;
}
the term
[<property>="<value>"]
restricts the application of the stylesheet to instances of the class whose <property> has the appropriate <value> the only caveat is that the style is not changed when the property changes its' value, so the stylesheet has to be reapplied for the look of the widget to actually change, see Stylesheet Documentation -> Property Selector
This construction moves the stylesheet into the widget that uses it and makes switch internal to the widget, the widget changes in accordance to its state.
In general you have a couple of ways to handle invalid inputs in your form
a) observe every change and update the style appropriately, you should be able to use QValidator for that too, but that is a separate topic, using QValidator you will probably be able to completely internalize the state of a single QTextEdit and not have to deal with its validity from the outside
b) Do it in the submit loop that you have described above, whenever the user clicks on submit change the state of the correct and incorrect fields
it all depends the structure of your app and the view
See, the other idea is you need to override the paint evet of line edit and then set the background image and color.
here the implimentation is presetn here button, follow up the same to your line edit

Resources