Solved QObject::setProperty() signalling solution?
-
I would subclass
QWidget
(sorry for C++):class PolishedWidget : public QWidget { Q_OBJECT Q_DISABLE_COPY(PolishedWidget) public: explicit PolishedWidget(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) : QWidget(parent, f) {} protected: bool event(QEvent* event) Q_DECL_OVERRIDE { if (event->type() == QEvent::DynamicPropertyChange) { style()->unpolish(this); style()->polish(this); } return QWidget::event(event); } };
Now all you need to do is change your other widgets to inherit from
PolishedWidget
rather thanQWidget
-
@VRonin
No, of course I would do this if I could!! :)I'm not defining my own widgets derived from
QWidget
. I'm using all the various Qt widgets,QLineEdit
,QPushButton
, etc., etc. They all of course already derive fromQWidget
, I'd like them to have derived fromPolishedWidget
but they don't!I'm saying I have gone through code and made it so I always create my own wrapper classes, one for each Qt widget class I use (like
MyLineEdit
just deriving fromQLineEdit
). So we have the luxury of a derived class for each widget class to put our code in. So we can do either:-
The way above, filtering the event, in each derived class's definition. Outside world will call
myLine.setProperty("class", ...)
. -
In each derived class's definition, supply a method, like
setProperty()
orsetPropertyClass()
or whatever, which includes callingpolish()
code itself (no event filtering). Outside world will callmyLine.setPropertyClass(...)
.
Do we prefer one approach over the other?
-
-
Do we prefer one approach over the other?
I do prefer the first option but I wouldn't subclass every single Qt Class.
In C++ You have 2 options (I'm 100% sure the second one is available to python too):
template <class T> class PolishedWidget : public T{ public: explicit PolishedWidget(QWidget* parent = Q_NULLPTR) : T(parent, f) {} protected: bool event(QEvent* event) Q_DECL_OVERRIDE { if (event->type() == QEvent::DynamicPropertyChange) { style()->unpolish(this); style()->polish(this); } return T::event(event); } };
Now you can create
PolishedWidget<QLineEdit> myLine;
andPolishedWidget<QPushButton> myButton;
Install an event filter:
class Polisher : public QObject { Q_OBJECT Q_DISABLE_COPY(Polisher) public: Polisher(QObject* parent = Q_NULLPTR) : QObject(parent) { } protected: bool eventFilter(QObject* obj, QEvent* event) Q_DECL_OVERRIDE { if (event->type() == QEvent::DynamicPropertyChange) { QWidget* objWidget = qobject_cast<QWidget*>(obj); if (objWidget) { objWidget->style()->unpolish(objWidget); objWidget->style()->polish(objWidget); } } return QObject::eventFilter(obj, event); } };
Now you can use:
Polisher* polisher = new Polisher(parent); QLineEdit* myLine =new QLineEdit(parent); QPushButton* myButton=new QPushButton(parent); myLine ->setEventFilter(polisher); myButton->setEventFilter(polisher);
-
@VRonin
For #1: Yeah, now the question is I wonder how/whether I'm supposed to dotemplate
class in Python/PyQt... !However, the point is you prefer event filter approach over explicit "set-property-with-call-to-polish" method. I'll go down that route.
For #2: I don't want to have to insert a line of a code to
setEventFilter
after each of thousands of lines of existing code, wherever any widget is created. Not to remember to do it in the future. Since I am now in a position where everyQLineEdit
orQPushButon
is already aJLineEdit
orJPushButon
[code only uses, say, 10 Qt widget types, so that's the sub-classing I've done], I can move the polishing code into its event filter and not insert loads of lines across loads of files. -
@JonB said in QObject::setProperty() signalling solution?:
now the question is I wonder how/whether I'm supposed to do template class in Python/PyQt
How: https://docs.python.org/3.6/library/typing.html#user-defined-generic-types
Whether: give it a try, and see if it works -
@VRonin
I have made the change (in my own way!) so that the widgets I use have an event filter on dynamic property changes to cause required polishing. I examineQDynamicPropertyChangeEvent::propertyName()
and do so only if it's one of mine (likeclass
). There are other cases too (e.g. I make setting/clearing read-only affect line edit's background color).How expensive do you think
polish
/unpolish()
(or for that matter re-assigningsetStyleSheet()
if I chose to do it that way) are?The approach we have chosen re-polishes every time I set some "property". However, somewhere in the docs it states we only need to do this when we change a property on something like an already-displayed widget.
Thus, if I set properties immediately after widget construction --- as I may well do --- I will do re-polishes for each one, even though none of them are necessary. OTOH, if I want to change the properties at a later date after widget has been shown, I would indeed need the re-polishing, but only then.
I haven't found all the circumstances, but I'm wondering whether my event filter could test something like
QWidget::isVisible()
before doing the re-polish, so that it does not do it when unnecessarily early in the widget's life-cycle. Any opinion/comment? -
@JonB said in QObject::setProperty() signalling solution?:
could test something like QWidget::isVisible() before doing the re-polish
You certainly could
-
@VRonin
Do you happen to know the "cost" of unpolish/polish? If it is "cheap", or perhaps "does nothing" on an unshown widget anyway, I won't worry my little head.... -
It is style dependant so there's no unique answer. On windows it looks to be relatively expensive if your widget is not visible
-
@JonB it should be at least less expensive than setStyleSheet