Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Detect top/parent move event on child widget



  • Hey

    I have QMainWindow with dockWidgets + many other sub widgets down the chain. How can I properly detect if a "on screen" position of a widget has changed on one of my child widgets?

    TIA

    Some code example

    from Widgets.widgetFollow.parentWidget import parentWidget
    
    import sys
    from PySide2.QtWidgets import *
    from PySide2.QtCore import *
    
    
    if __name__ == '__main__':
        # Create the Qt Application
        app = QApplication(sys.argv)
        # Create and show the form
    
        ma = QMainWindow()
        ma.setCentralWidget(parentWidget())
    
        ma.show()
    
        sys.exit(app.exec_())
    
    import sys
    from PySide2.QtWidgets import *
    from PySide2.QtCore import *
    
    
    class childWidget(QWidget):
        def __init__(self, parent):
            super(childWidget, self).__init__()
            self.p = parent
    
        def moveEvent(self, event):
            # print("Move event :", event)
            self.newChildPos(event.pos())
    
    
    class parentWidget(QWidget):
        def __init__(self):
            super(parentWidget, self).__init__()
    
            self.cPos = (0, 0)
            lay = QGridLayout()
            self.setLayout(lay)
            self.b = QPushButton()
            lay.addWidget(self.b)
    
            self.w = QWidget()
            self.w.setGeometry(200, 200, 300, 300);
            self.setGeometry(500, 500, 200, 200);
    
            self.w.show()
    
            self.b.released.connect(self.handlePosReset)
    
        def changeEvent(self, event):
            print("Change event :", event)
    
        def moveEvent(self, event):
            delta = event.pos() - event.oldPos()
            self.w.move(self.w.pos() + delta)
    
        def resizeEvent(self, event):
            # print("REsize event :", event)
            delta = event.size() - event.oldSize()
            print(self.mapToGlobal(self.b.pos()))
    
        def newChildPos(self, pos):
            self.cPos = pos
    
        def handlePosReset(self):
            x = self.mapToGlobal(self.b.geometry().topRight()).x()
            y = self.mapToGlobal(self.geometry().topRight()).y()
            print(x, y)
            print(self.geometry().topRight())
            self.w.move(x, self.mapToGlobal(self.geometry().topRight()).y())
            self.w.show() # not working ;./ 
            self.w.setFocusPolicy() # not working ;/
    
    


  • You can use an EventFilter for this:

    https://doc.qt.io/qt-5/qobject.html#installEventFilter



  • @gde23 where do you want me to install it on ? On the child widget?
    Installing it on childWidget did not do anything. There are no events popping up as far as I can tell..


  • Lifetime Qt Champion

    @Dariusz
    Hi
    Yes on the child Widgets as an alternative to subclass them all and use moveEvent directly.
    https://doc.qt.io/qt-5/qmoveevent.html#details

    Are you saying the no such event is posted to the filter when the widgets are moved ?

    update:
    very fast test

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->frame->installEventFilter(this);
    }
    
    bool MainWindow::eventFilter(QObject *watched, QEvent *event)
    {
    
        qDebug() << event->type();
    
        return QMainWindow::eventFilter(watched, event);
    }
    
    void MainWindow::on_pushButton_released()
    {
    
        ui->frame->move(200, 200);
    }
    
    

    Place a Frame on form
    Move it with a button
    it does report Move event

    QEvent::Polish
    QEvent::Move
    QEvent::Resize
    QEvent::Show
    QEvent::ShowToParent
    QEvent::PolishRequest
    QEvent::UpdateLater
    QEvent::WindowActivate
    QEvent::Paint
    QEvent::Paint
    QEvent::Enter
    QEvent::Leave
    QEvent::WindowDeactivate
    QEvent::Paint
    QEvent::Paint
    QEvent::WindowActivate
    QEvent::Paint
    QEvent::Paint
    QEvent::Move <<<<< the actual move via the button
    QEvent::Paint
    QEvent::WindowDeactivate
    QEvent::Hide
    QEvent::Hide



  • @mrjj said in Detect top/parent move event on child widget:

    Are you saying the no such event is posted to the filter when the widgets are moved ?

    As far as I can tell ye, have a look at this main

       ma = QMainWindow()
        ma.setCentralWidget(parentWidget())
    

    parentWidget.moveEvent() never gets fired.
    I don't mind subclassing widgets. But they don't get even moveWindow when I move mainWindow. - ma


  • Qt Champions 2019

    @Dariusz said in Detect top/parent move event on child widget:

    But they don't get even moveWindow when I move mainWindow

    Why should the children get a moveWindow event when only the parent moves? The children don't move then (relative) so no event is needed.



  • @Christian-Ehrlicher Hence my question... How can I properly detect if a "on screen" position of a widget has changed on one of my child widgets?
    :- )))


  • Qt Champions 2019

    You already answered this by yourself - you know when the parent moved, so the absolute position of the child must have changed too...


  • Lifetime Qt Champion

    Hi
    Ahh. now i get what you ask. The reverse.
    But why would you need to know that?
    the children will move with parents automatically.



  • @mrjj I have created a "new" floating widget. That user can move anywhere to screenn, but at the same time widget will move in relation to specified X widget. For example it will be a button in future. Now when button moves that can be a child of Widget>dockWidget>mainWindow changing position of mainWindow, will move button to new position thus my hover widget has to move as well. Same goes if user change dockWidget position... and there can be nested hierarchy like docket>docked>docked/etc/etc... so I'm looking if there is any way at all to be notified if a widget change its screen position at any time...

    The example above shows that. where self.w is my floating widget that is meant to follow parentWidget... but when parent widget is a centralWidget of mainWindow... then the self.w no longer gets notified to change its positionw hen parentWIdget gets moved via its parent...



  • @Christian-Ehrlicher said in Detect top/parent move event on child widget:

    You already answered this by yourself - you know when the parent moved, so the absolute position of the child must have changed too...

    I'm not sure... as far as I can tell I have to recrusively notify all widgets children+their children+their children of position/size/ change and let them all recalculate properly.... but I'm not sure if thats the best approach...


  • Lifetime Qt Champion

    @Dariusz said in Detect top/parent move event on child widget:

    as far as I can tell I have to recrusively notify all widgets children+their children+their children of position/size/ change and let them all recalculate properly

    That's what Layouts normally are for. Automatic resize when parent is resized.

    Did you use layouts for the children ?



  • @mrjj Yes I did, so I take I could listen to layout resizeevent/etc. But what about moveEvent?


  • Qt Champions 2019

    @Dariusz said in Detect top/parent move event on child widget:

    But what about moveEvent?

    Again: they do not move - so there is no move event!



  • @Christian-Ehrlicher So how can I detect it? Or I have to travers entire hierarchy ?


  • Qt Champions 2019

    This was already answered.


  • Lifetime Qt Champion

    @Dariusz said in Detect top/parent move event on child widget:

    @mrjj Yes I did, so I take I could listen to layout resizeevent/etc. But what about moveEvent?

    well, you can add a new signal to the top parent and then hook up that signal
    to all children. then in
    parent moveEvent emit the signal to tell the children

    But i cannot for the life of me understand why you need that.
    If using layouts, the child will scale with Parent and a child is
    NOT moved when a parent is moved. it stays in same position inside the parent. So no need for it to know it at all.



  • @mrjj Did you look at the example above? The childWidget is not actually in layout or anything like that... he does not even have parent. Hes being moved by parentWidget() inside moveEvent... I'm trying to affect non-related widget... MoveEvent() moves its correctly... except that it does not when the parentWidget is inside another widget... So I'm trying to get that example code above work. Without having to recursively spam hundreds of widgets... Perhaps I should not name them childWidget/parentWidget :/



  • @Christian-Ehrlicher said in Detect top/parent move event on child widget:

    This was already answered.

    Are you reffering to this?

    @Christian-Ehrlicher said in Detect top/parent move event on child widget:

    You already answered this by yourself - you know when the parent moved, so the absolute position of the child must have changed too...

    What signal/slot/function does get called on child to notify it of new parent position ?


  • Qt Champions 2017

    @Dariusz said in Detect top/parent move event on child widget:

    What signal/slot/function does get called on child to notify it of new parent position ?

    None. If a widget is moved in relation to its parent's client rect (for top-level windows the "parent's client rect" is the screen), then it's going to receive a move event. Listen to that event, if you wish, by means of installing an event filter, as already mentioned.

    There's no signal/slot/function that gets called on the children to notify of move, because that makes no sense. The parent moves, hence the rect of the widget (the non-client rect) moves, but children's rects are still left exactly the same relative to their parent's rect, so they ain't moving, hence no move event.


  • Lifetime Qt Champion

    @Dariusz
    Ahhhhhhhhhhh. We are talking floating widgets here :)
    That explains a lot.

    When you say "child" it implies it has a parent that Widget acting as a window
    never has. (QDialog types being an exception)

    So you on the right way with moveEvent and some signal.

    • except that it does not when the parentWidget is inside another widget.
      Which would make it a child. :) but in this case, it will move with the parent automatically.

    In any case, you should be aware of
    QApplication::topLevelWidgets()

    that will give a list with all Windows types. those are the one, you want to move manually.



  • @mrjj Yes I have totally failed. I should have not called it parent/child as it confused every1 and no1 has read the code :- )))

    So I'll try to rephrase the question here...

    Problem A:
    I have 2 widgets.
    WidgetA is a data widget, say QGraphicsView. - this widget can be inside layout of another widget.
    WidgetB is a floating window, with tools.

    The idea is that whenever WidgetA moves, the widgetB should move with it - as well as being able to move by itself - it's all in the python example above...

    The problem is, that if WidgetA is inside layout, then it no longer informs widgetB of the new position because it don't get move events.

    Current working "approach" that I can think off is that I have to subclass all of my widgets, and reimplement their moveEvent() & inform each child widget of new position so that they can properly handle their positioning in relationship to new parent position.

    On another side I'm also thinking of implementing a qApp->eventFilter() - a global one, then look for moveEvent() in there and emit signals to all registered widgets from that...

    Given that my app is "dynamically" build, meaning that parenting/widget hierarchy can change at any time, I'm looking for "automated" approach, hard coding stuff won't work here :- )

    Problem B: (just emerged)
    How do I et WidgetB Z depth, to be always +1 over its WidgetA so that it can be always visible above WidgetA but not visible if WidgetX moves mover it... ?

    TIA :- )


  • Lifetime Qt Champion

    @Dariusz said in Detect top/parent move event on child widget:

    Oh yes. Words can be deceiving. I did read the code but since you said child widget i still
    didnt get why you needed any code at all :)

    The problem is, that if WidgetA is inside layout, then it no longer informs widgetB of the new position because it don't get move events.

    But why must it be WidgetA ? should dit not be the top widget (the window) ?
    Or the issue is that sometimes
    WidgetA is a window and life is good, but then same class (WidgetA ) is used in a layout and then suddenly
    it cant inform widgetB any more since only top parent knows it was moved ?

    Something like that ?



  • I'm sorry, my bad :- ((

    @mrjj said in Detect top/parent move event on child widget:

    @Dariusz said in Detect top/parent move event on child widget:
    Or the issue is that sometimes
    WidgetA is a window and life is good, but then same class (WidgetA ) is used in a layout and then suddenly
    it cant inform widgetB any more since only top parent knows it was moved ?
    Something like that ?

    Yes, since its dynamically generated UI based on a user template, the WidgetA can be inside layout of another widget, or be by itself.