You may know I am a big fan of Qt sylesheets. While playing around with them I stumbled across a subtle behavior which had me puzzled for a while.
Often when I need to slightly override the behavior of a widget, I do not create separate .h/.cpp files for them. Instead, I write the code directly in the .cpp file where this widget will be used.
Here is an example, assuming SomeWidget is a widget used elsewhere in the application, and it contains an instance of a custom widget of class InternalWidget.
This is SomeWidget.h:
class SomeWidgetPrivate;
class SomeWidget : public QWidget {
public:
SomeWidget(QWidget* parent = 0);
/* Rest of SomeWidget declaration */
private:
SomeWidgetPrivate* const d;
};
And this is SomeWidget.cpp:
class InternalWidget : public QWidget {
protected:
virtual void mousePressEvent(QMouseEvent*) {
/* Custom code here */
}
};
struct SomeWidgetPrivate {
InternalWidget* mInternalWidget;
};
SomeWidget::SomeWidget(QWidget* parent)
: QWidget(parent)
, d(new SomeWidgetPrivate) {
d->mInternalWidget = new InternalWidget(this);
}
So far so good, I create InternalWidget inside SomeWidget and it works well. Now let’s say I want to apply a stylesheet to SomeWidget by calling setStylesheet() on it. Qt allows me to define styles for classes, so if I want to paint the background of InnerWidget instances in blue, I can write a stylesheet like this:
InternalWidget {
background-color: blue;
}
Alas, it doesn’t work. After scratching my head for a while, I realized that Qt stylesheet system probably uses the metaobject information to know the exact class of a widget instance. By defining my internal widget the way I did, moc didn’t have a chance to run on it, so mInternalWidget appeared like an instance of QWidget, not InternalWidget.
To get the stylesheet to apply, one must get moc to run on the widget classes, so it’s important to:
- Declare the widget in a header file
- Add the Q_OBJECT macro (otherwise moc while happily ignore it)
I hope this saves you some scratch-your-head time
Update: See Tom M, Girish and Matthias Kretz comments for other ways to avoid this bug without moving the class to a separate header file.
CSS in Qt is EVIL avoid it as the plague!
@aac Care to elaborate?
You can have the Q_OBJECT macro in .cpp only file too. You just have to have moc run on foo.cpp and add #include “foo.moc” in the .cpp
It overrides the style and can thus lead to a look which is inconsistent with the rest of the system.
Can’t CSS stuff be based on objectName?
You don’t need to have the class in a separate header file. Just put the Q_OBJECT macro as always, and also add a #include “internalwidget.moc” at the end of cpp file.
“qmake -project” runs moc on cpp files if it files such an include line
You can also use what Ian suggested. Give all the InternalWidget the same object name (unlike html, object id’s needn’t be unique in Qt).
The automoc stuff in kdesupport allows you to also keep the Q_OBJECT using class declaration in the cpp file. See
http://techbase.kde.org/Development/Tools/Automoc4#Features
for the details.
@Tom M, Girish, Matthias Kretz: thanks for the information, didn’t know that. Will update my post.
@Ian Monroe: yes CSS can use objectName, but the purpose of it is different: it’s here to style a specific widget instance, not a whole class.
@Albert Astals Cid, Kevin Kofler: CSS is not necessary evil, it’s a new tool, you just need to use it correctly to ensure it integrates well with the rest of the application. It’s also useful in places where you want to provide a different appearance (like Gwenview fullscreen mode)
warning: ‘SomeWidgetPrivate first seen as class, then as struct’
@jstaniek: interesting, gcc does not complain when I write this, is msvc pickier? (I know you work on kde-win32
)