Search code examples
pythonpython-3.xpyqt5qmainwindowqdockwidget

Get reference of dock-widget added to QMainWindow


I use a software written using Qt and it has a QMainWindow object. I need to get the reference of a widget when added to QMainWindow object already exists. I have looked at Qt documentation. I hoped there was an event childAdded or something, but I couldn't find anything.

Please review this code:

import sys
from PyQt5.QtWidgets import QApplication, QPushButton, QMainWindow, QDockWidget
from PyQt5.QtCore import Qt

class MainWindow(QMainWindow):    
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)    
        self.button = QPushButton("A Button!")
        self.setCentralWidget(self.button)

def do_something(w):
    w.setWindowTitle("Newly Added")

def add_dock_widget(win):
    win.addDockWidget(Qt.RightDockWidgetArea, QDockWidget())

app = QApplication(sys.argv)
window = MainWindow()
window.show()

add_dock_widget(window)
# At this point, somehow, I need to get the reference of added widget.
# Because add_dock_widget method doesn't return any reference.

# I'm looking for something like that, so that I can manipulate the widget:
# window.childAdded[****].connect(do_something)

app.exec_()

The software allows to write python script in it. When a child is added to GUI, I need to change something within the child using script.


Solution

  • Depending on your background goal there are several alternatives:

    - findChild() or findChildren():

    If you know the object class you can use findChild or findChildren:

    if __name__ == '__main__':
    
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
    
        add_dock_widget(window)
        dockwidget = window.findChild(QDockWidget)
        do_something(dockwidget)
    
        # or
        # for dockwidget in window.findChildren(QDockWidget)
        # do_something(dockwidget)
    
        app.exec_()
    

    But this method is limited since you can have many widgets of the same type and you could not distinguish which object is the one you want so you will have to do some new filter passed in another feature.

    - Override the event method or use an event filter:

    class MainWindow(QMainWindow):    
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)    
            self.button = QPushButton("A Button!")
            self.setCentralWidget(self.button)
    
        def event(self, e):
            if e.type() == QEvent.ChildAdded:
                if isinstance(e.child(), QDockWidget):
                    do_something(e.child())
            return super(MainWindow, self).event(e)
    
    class ChildAddedFilter(QObject):
        def __init__(self, widget):
            super(ChildAddedFilter, self).__init__(widget)
            self._widget = widget
            self._widget.installEventFilter(self)
    
        def eventFilter(self, o, e):
            if o is self._widget and e.type() == QEvent.ChildAdded:
                if isinstance(e.child(), QDockWidget):
                    do_something(e.child())
            return super(ChildAddedFilter, self).eventFilter(o, e)
    
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        window = MainWindow()
        f = ChildAddedFilter(window)
        window.show()
        add_dock_widget(window)
        app.exec_()