What is more efficient for custom widget: Re-implementing protected QEvent functions or chaining signals?
-
Hello to all. I am subclassing a
QPushButton
asPushButton
like this:class Shape //irrelevant for now class PushButton : public QPushButton { Q_OBJECT public: explicit PushButton( QWidget* parent, Shape* shape); .... signals: //! Custom signal to emit the clicked PushButton into slots /*! * The classic `QPushButton::clicked(bool)` is not sufficient in this case. * We need a signal that will cary the `PushButton*` that triggered it. */ void pbtnClicked( PushButton* pbtn ); };
I need to use
pbtnClicked
signal to pass thePushButton*
(ie thethis
that was clicked) parameter in the ctor of another object.I am thinking of two ways to do this:
A) Chaining signals like:
// pushbutton.cpp PushButton::PushButton( QWidget* parent, Shape* shape ) : QPushButton{ dynamic_cast<QWidget*>( parent ) } , m_shape{ new Shape*( shape ) } { ... connect( this, &PushButton::clicked, //chain the inherited functionality this, [this]() -> void { emit pbtnClicked( this ); }, // to the custom signal Qt::DirectConnection ); }
or B) by using the event system like:
// Ok, this doesn't work if PushButton was clicked with the enter key bool PushButton::eventFilter( QObject* watched, QEvent* event ) { // The watched object has to be a PushButton* (ie *this* PushButton) if ( watched == this && event->type() == QEvent::MouseButtonRelease ) { auto* mouseEvent = dynamic_cast<QMouseEvent*>( event ); if ( mouseEvent->button() == Qt::MouseButton::LeftButton ) { emit pbtnClicked( this ); return true; } } return false; } ); }
Is the second way more low-lever and thus more appropriate to customize widgets? Is it faster that chaining signals?
Which way would you choose and if you chose B would you use another function like
bool QAbstractButton::hitButton(const QPoint &pos) const
?Thank you for your comments and critic.
EDIT: Typo.
-
Hi
The chaining of signals is most like the most efficient as
the eventfiler will see all events and hence do compare to all of them.The emit is more direct,
However, event filter might be more handy if you already have used lots of puthbutton in a Design and
its huge task to replace them with the subclass.However, the PushButton subclass keeps it all neatly contained and I would choose this over
event filter if starting a new design. -
@mrjj Thanks for the insightfull answer. Indeed
eventFilter
will see every event. What would be a more suitable function? I am thinking ofmousePressEvent
andkeyPressEvent
to look for left click and enter key respectively.I 've read that a signal is used and connected to from some user of the class, while the event system is used to further customize the custom widget (from the developer I suppose). What is your opinion on this?
Thank you again for your time.
-
But I don't see why you need to subclass QPushButton at all here. Either use QObject::sender() or a simple lambda in the connect to get the sender.
-
@Christian-Ehrlicher
PushButton
's ctor accepts aShape*
NowShape
is the base class class forHex
,TBar
,Pipe
etc. This way eachPushButton
has aShape**
member and when it is passed to another object, I can use various virtual functions fromShape
depending if I passed aHex
,Pipe
or something else.Although I haven't worked with
QObject::sender()
I can't think of another way for aQPushButton
to holdShape
-related information but to subclass it and emit the derived object so I can access all theShape
virtual functions.CalculatorWindow::CalculatorWindow( QWidget* parent ) : QMainWindow{ parent } , m_ui{ new Ui::CalculatorWindow() } , mListOfButtons{ QList<PushButton*>() } , m_btnPipe{ new PushButton{ this, new Pipe{ this } } } , m_btnTBar{ new PushButton{ this, new TBar{ this } } } , m_btnHexa{ new PushButton{ this, new Hex{ this } } } ....
Later in another widget, I am constructing a
ShapeView
like this:// shapeview.h class ShapeView: public QGraphicsView { Q_OBJECT public: ShapeView( const PushButton* pBtn, QWidget* parent = nullptr ); .... // shapeview.cpp ShapeView::ShapeView( const PushButton* pBtn, QWidget* parent ) : QGraphicsView{ pBtn->shapeType()->scene()->p_scene, parent } { setWindowTitle( tr( pBtn->shapeType()->name().c_str() ) ); }
I am suspecting that I could subclass
QPushButton
and usesender()
but I haven't experimented with it (yet).Thank you for your answer, it might help me do things simpler.
-
Hi
- I 've read that a signal is used and connected to from some user of the class, while the event system is used to further customize the custom widget (from the developer I - suppose). What is your opinion on this?
Well often signal and slots are used as a defined interface that the user of the class can connect to, to trigger an action or
get notified of some state changes.Events are often more system orientated and more often changed to alter the default behavior of the class in regards to the
events send by the OS or Qt.Both can be used to costimize a class. What do use , mostly depends on what needs to change.
In your case, adding a new Click signal, it pretty fine to do the way you did.However, does your PushButton use shape for anything ?
Im asking as if button dont use it for anything, you could store it in a dynamic property and use the sender() to get the button.
and you would not need new signal or anything else.However, if button does access the shape, then a sublass seem better.
-
@mrjj said in What is more efficient for custom widget: Re-implementing protected QEvent functions or chaining signals?:
However, if button does access the shape, then a sublass seem better.
It does, yes. Actually it's the only reason this subclass was created in the first place; Each
PushButton
must have a different shape but with a unique interface (virtual ShapeScene* Shape::scene()
,name()
etc).Regarding the sender() function, I came up with this:
//Experimental connect(pbtn, &PushButton::clicked, this, [this](){showDialog(qobject_cast<PushButton*>(sender()));}); ... void CalculatorWindow::showDialog( PushButton* pbtn ){ ...
Earlier it used to be less "hacky" because
pbtnClicked(PushButton* pbtn)
signal carried what I needed. Also, Qt states that sender() ...... function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
I don't have many signals connected to
showDialog(PushButton*)
but I get the same result. So it boils for me to learn good habits now that I am more inexperienced.Anyway thank you all for your good answers. I certainly learned something here :)
-
Hi
Just as a note.
When using a lambda, one can also just capture the buttonconnect(pbtn, &PushButton::clicked, this, [this, pbtn](){showDialog(pbtn);});
but for a normal/classic slot, the sender() is needed for same trick.
-
Hi,
This looks a bit convoluted. Why not use a QMap between QPushButton and your shape class ?
-
@mrjj said in What is more efficient for custom widget: Re-implementing protected QEvent functions or chaining signals?:
Hi
Just as a note.
When using a lambda, one can also just capture the buttonconnect(pbtn, &PushButton::clicked, this, [this, pbtn](){showDialog(pbtn);});
but for a normal/classic slot, the sender() is needed for same trick.
I think you are right, yes.
For your next comment, yes it's a bit messy. It all started when PushButtons couldn't work with
QButtonGroup
and I had to subclass this too. Anyway, as soon as I fix some nasty valgrind/memory errors I will post the link for review and critic :)This program can certainly do things way simpler.
-