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!
-
# 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?
-
Hi,
Are you trying to implement some sort of action editor ?
-
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 thatKeyboardShortcutsDialog
is tied to showing theQAction
s 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 toKeyboardShortcutsDialog
, which in turn can pass it on toKeyboardShortcutsTable
? Do you requireKeyboardShortcutsDialog
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 ofQAction
s instead of aQWidget
, depending on whether I really need to access the widget rather than just its actions. -
@NonNT
Well you can if you specify the correct parentage. If you are insideKeyboardShortcutsTable
you presumably have to go up to itsKeyboardShortcutsDialog
, and then from there up to theMainWindow
.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.