Tap into QWidget::create


  • Qt Champions 2016

    Hello,
    I have a push button dial custom widget derived from QStackedWidget and I wish to lay it out (create the buttons) depending on the QAction instances attached to the widget. Currently I do that in a separate function that initializes the widget, but I want that to be done after the widget is created. Unfortunately, QWidget::create is not virtual so reimplementing it seems impossible, are there any workarounds? Could I use one of the change events handled in QWidget::changeEvent instead?

    Kind regards.


  • Moderators

    @kshegunov
    there is a QEvent::ActionAdded (QActionEvent) event which is fired to the widget where addAction() is called on.
    Does this help you?


  • Qt Champions 2016

    @raven-worx
    Hello,
    I don't see how I could use it, but maybe I shouldn't have been so laconic in my original post. I'll give the whole picture and hopefully it'll become clear what and why exactly I'm needing this. I have a push button dial but it's not "flat" as one might ordinarily expect. What I mean is that some of the buttons are actually opening another dial with another set of buttons and a return button. The tree-like behaviour is similar to what you might expect from a menu. Some actions have subactions and some subactions can have subactions and so on. This is the reason I'm actually deriving from QStackedWidget and am not initializing the dial ui in the ordinary fashion (with a form), but instead am building the pages recursively. The hierarchy of actions I simply build through QObject's parent, in the conventional manner. Everything is fine and dandy, except I have to explicitly call my method that creates the stacked pages, the layouts and the buttons based on the actions. This is what I'd wish to avoid. Obviously I couldn't do it in the constructor of my widget, since there are no actions added at that point. I was thinking to maybe intercept the show event and do a lazy init there, but was unsure if there isn't a better way. It just occurred to me while typing that your suggestion might be viable if I build the pages incrementally at each ActionAdded event, so I'll try that and if in the meantime anyone has another idea I'd appreciate it.

    Thanks for the help!
    Kind regards.


  • Moderators

    @kshegunov
    analog there is also a QEvent::ChildAdded event (QChildEvent) which is triggered when a QObject parent/child relation is created.

    Something like this?

    void childEvent(QChildEvent * event)
    {
          BaseClass::childEvent( event );
    
          if( event->added() )
          {
                   if( MyObj* obj = qobject_cast<MyObj*>( event->child() ) )
                   {
                            // add page
                   }
          }
    }
    

  • Qt Champions 2016

    @raven-worx
    Hm, this also could possibly be useful to monitor the QAction instances for changes in the hierarchy, but for this I'll have to install an event filter on each of them, which I'm reluctant to do. Still, I might go with it in the end. Thanks!

    Kind regards.


  • Qt Champions 2016

    @raven-worx
    Hello,
    The QEvent::ActionAdded worked wondrously, however your second suggestion with QEvent::ChildAdded is a bit more involved. I have put an event filter to each of my attached actions to capture the ChildAdded/ChildRemoved events but the handling of these events should be scheduled through the event loop, because at the point where QWidget::childEvent is invoked the object returned by event->child() is not completely constructed! I'm writing this only to let you know that this line MyObj* obj = qobject_cast<MyObj*>( event->child() ) will not work and for further reference to anyone reading this thread. Still, I thank you for the input, it helped me quite a lot!

    Kind regards.


  • Moderators

    @kshegunov
    a workaround for this would be not to continue processing with the child pointer immediately , but access it delayed:

    void childEvent(QChildEvent * event)
    {
          BaseClass::childEvent( event );
    
          if( event->added() )
          {
                   QMetaObject::invokeMethod( this, "childAdded", Qt::QueuedConnection,  Q_ARG(QObject*,event->child() );
          }
    } 
    
    // this method must be a SLOT or have the Q_INVOKABLE macro set
    void childAdded( QObject* child )
    {
          MyObj* obj = qobject_cast<MyObj*>( chidl );  // fully constructed by now
    }
    

  • Qt Champions 2016

    @raven-worx
    Hello,
    Thanks for the code, but I know how to do the scheduling. I actually went with QPointer<QObject> since the QObject might be deleted while waiting in the event loop. Like this:

    bool AgDial::eventFilter(QObject * object, QEvent * event)
    {
    	switch (event->type())
    	{
    	case QEvent::ChildAdded:
    		QMetaObject::invokeMethod(this, "scheduleChildAdd", Qt::QueuedConnection, Q_ARG(QPointer<QObject>, QPointer<QObject>(reinterpret_cast<QChildEvent *>(event)->child())));
    		break;
    	case QEvent::ChildRemoved:
    		d()->detachAction(reinterpret_cast<QChildEvent *>(event)->child());
    		break;
    	}
    
    	return QStackedWidget::eventFilter(object, event);
    }
    

    Thanks for taking the time though!

    Kind regards.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.