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

How to implement findChildren() universally ?



  • Hey!

    MainWindow imports Dialog which imports CustomWidget

    In CustomWidget the MainWindow is the grandparent:

    actions = self.parentWidget().parentWidget().findChildren(QAction)
    

    MainWindow imports CustomWidget

    In CustomWidget the MainWindow is the parent:

    actions = self.parentWidget().findChildren(QAction)
    

    How to implement findChildren() so that it works in both cases?

    Thanks



  • @NonNT
    There i something wrong here. Either something is a parent or a grandparent or whatever, but what you import makes no difference either way. Parentage is to do with how widgets are added where, not by what is imported.

    Having said that: there is (almost certainly) something wrong with your logic if from within a child/custom widget you are reaching up to its parent/grandparent and doing something there, like looking for children. Essentially widgets should not need to know anything about their parent; they may look at their own children, but should no be relying on their parent being anything in particular. Urge you to reconsider your approach, before your code turns into a spaghetti-mess!



  • main.py

    # This Python file uses the following encoding: utf-8
    
    import sys
    from PySide2.QtWidgets import QApplication
    
    from main_window import MainWindow
    
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        app.setOrganizationName('pyTest')
        app.setApplicationName('Keyboard Shortcuts')
        app.setApplicationDisplayName('Keyboard Shortcuts')
    
        window = MainWindow()
        window.show()
    
        sys.exit(app.exec_())
    

    main_window.py

    # This Python file uses the following encoding: utf-8
    
    import sys
    
    from PySide2.QtCore import Qt
    from PySide2.QtGui import QIcon, QKeySequence
    from PySide2.QtWidgets import QAction, QApplication, QMainWindow, QMenu
    
    from keyboard_shortcuts_dialog import KeyboardShortcutsDialog
    from keyboard_shortcuts_table import KeyboardShortcutsTable
    
    
    class MainWindow(QMainWindow):
    
        def __init__(self):
            QMainWindow.__init__(self)
    
            self.resize(QApplication.desktop().availableGeometry(self).width() / 2, QApplication.desktop().availableGeometry(self).height() / 2);
            self.move((QApplication.desktop().availableGeometry(self).width() - self.width()) / 2, (QApplication.desktop().availableGeometry(self).height() - self.height()) / 2);
    
            actionQuit = QAction('Quit', self)
            actionQuit.setIcon(QIcon.fromTheme('application-exit'))
            actionQuit.setShortcut(QKeySequence.Quit)
            actionQuit.setToolTip('Quit the application')
            actionQuit.triggered.connect(self.close)
    
            actionKeyboardShortcuts = QAction('Keyboard Shortcuts', self)
            actionKeyboardShortcuts.setIcon(QIcon.fromTheme('help-keybord-shortcuts'))
            actionKeyboardShortcuts.triggered.connect(self.onActionKeyboardShortcutsTriggered)
    
            menu = self.menuBar().addMenu('Application')
            menu.addAction(actionKeyboardShortcuts)
            menu.addSeparator()
            menu.addAction(actionQuit)
    
            # Main widget
            self.setCentralWidget(KeyboardShortcutsTable(self))
    
    
        def onActionKeyboardShortcutsTriggered(self):
    
            dialog = KeyboardShortcutsDialog(self)
            dialog.setWindowTitle('Keyboard Shortcuts')
            dialog.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
            dialog.show()
    

    keyboard_shortcuts_dialog.py

    # This Python file uses the following encoding: utf-8
    
    from PySide2.QtWidgets import QApplication, QDialog, QDialogButtonBox, QVBoxLayout
    
    from keyboard_shortcuts_table import KeyboardShortcutsTable
    
    
    class KeyboardShortcutsDialog(QDialog):
    
        def __init__(self, parent=None):
            super(KeyboardShortcutsDialog, self).__init__(parent)
    
            self.resize(QApplication.desktop().availableGeometry(self).width() / 3, QApplication.desktop().availableGeometry(self).height() / 3);
            self.move((QApplication.desktop().availableGeometry(self).width() - self.width()) / 2, (QApplication.desktop().availableGeometry(self).height() - self.height()) / 2);
    
            buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
            buttonBox.rejected.connect(self.close)
    
            # Main layout
            layout = QVBoxLayout()
            layout.addWidget(KeyboardShortcutsTable(self), 1)
            layout.addWidget(buttonBox)
    
            self.setLayout(layout)
    

    keyboard_shortcuts_table.py

    # This Python file uses the following encoding: utf-8
    
    from PySide2.QtCore import Qt
    from PySide2.QtGui import QKeySequence
    from PySide2.QtWidgets import QAction, QMainWindow, QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget
    
    
    class KeyboardShortcutsTable(QWidget):
    
        def __init__(self, parent=None):
            super(KeyboardShortcutsTable, self).__init__(parent)
    
            labels = ['Name', 'Shortcut', 'Description']
    
            shortcutItems = []
    
            # Only works if MainWindow is parent
            actionItems = self.parentWidget().findChildren(QAction)
    
            # Only works if MainWindow is grandparent
            #actionItems = self.parentWidget().parentWidget().findChildren(QAction)
    
            for actionItem in actionItems:
                if not actionItem.shortcut().isEmpty():
                    shortcutItems.append(actionItem)
    
            tableBox = QTableWidget(len(shortcutItems), len(labels), self)
            tableBox.setHorizontalHeaderLabels(labels);
            tableBox.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
            tableBox.horizontalHeader().setStretchLastSection(True)
            tableBox.verticalHeader().setVisible(False)
    
            row = 0
            for shortcutItem in shortcutItems:
                tableBox.setItem(row, 0, QTableWidgetItem(shortcutItem.text()))
                tableBox.setItem(row, 1, QTableWidgetItem(shortcutItem.shortcut().toString(QKeySequence.NativeText)))
                tableBox.setItem(row, 2, QTableWidgetItem(shortcutItem.toolTip()))
                row += 1
    
            # Main layout
            layout = QVBoxLayout()
            layout.addWidget(tableBox, 1)
    
            self.setLayout(layout)
    

    <--->

            # Only works if MainWindow is parent
            actionItems = self.parentWidget().findChildren(QAction)
    
            # Only works if MainWindow is grandparent
            #actionItems = self.parentWidget().parentWidget().findChildren(QAction)
    

    So how can this be done universally?


  • Lifetime Qt Champion

    Hi,

    Are you trying to implement some sort of action editor ?



  • No, it is just a simple text editor. I want to list all available Keyboard Shortcuts in a dialog.
    But I want to write KeyboardShortcutsTable so that I can use it elsewhere.

    keyboard-shortcuts-dialog.png



  • I have changed it like this:

    class KeyboardShortcutsTable(QWidget):
        def __init__(self, mainWindowWidget, parent=None):
            ...
            actionItems = mainWindowWidget.findChildren(QAction)
            ...
    
    class KeyboardShortcutsDialog(QDialog):
        ...
        layout.addWidget(KeyboardShortcutsTable(self.parentWidget()), 1)
        ...
    

    It works, but I am not sure if it is the best solution.



  • @NonNT
    This means that KeyboardShortcutsDialog is tied to showing the QActions of whatever its parent widget is. Which is not bad, but if you're asking me I would remove that dependency.

    You are already passing the desired widget as a parameter to your KeyboardShortcutsTable, which is good. Why not take it a step further, and pass it as a parameter to KeyboardShortcutsDialog, which in turn can pass it on to KeyboardShortcutsTable? Do you require KeyboardShortcutsDialog to only be able to show the shortcuts of its own parent widget, rather than of any widget passed to it?

    class MainWindow(QMainWindow):
        def onActionKeyboardShortcutsTriggered(self):
            dialog = KeyboardShortcutsDialog(self, self)
    
    
    class KeyboardShortcutsDialog(QDialog):
        def __init__(self, actionsWidget, parent=None):
            ...
            self.actionsWidget = actionsWidget
            ...
            layout.addWidget(KeyboardShortcutsTable(self.actionsWidget), 1)
            ...
    
    
    class KeyboardShortcutsTable(QWidget):
        def __init__(self, actionsWidget, parent=None):
            ...
            actionItems = actionsWidget.findChildren(QAction)
            ...
    

    No parenting-dependencies here! I might pass actionsWidget.findChildren(QAction) as the parameter, receiving a list of QActions instead of a QWidget, depending on whether I really need to access the widget rather than just its actions.



  • KeyboardShortcutsTable is tied to the parent widget.
    I thought that you could call the findChildren(QAction) or something similar in KeyboardShortcutsTable to get the list of actions without passing the MainWindow widget.
    But it works now. Thanks



  • @NonNT
    Well you can if you specify the correct parentage. If you are inside KeyboardShortcutsTable you presumably have to go up to its KeyboardShortcutsDialog, and then from there up to the MainWindow.

    But I think its preferable to pass the MainWindow widget explicitly, then your table is not tied to where it happens to be situated/called from.


Log in to reply