In the code below, I have some elements placed in QtDesigner, and then a couple of empty QFrames, named my_widget_01
, and my_widget_02
:
I have promoted these to a MyWidget class, which basically just adds a mock-up label in the Python code:
Now, what I would like to do, is to "find" these custom objects - either by name, or by class - as a list; but for some reason I can't. As you can see in the code below, if I run self.findChildren(QtWidgets.QFrame)
it finds a bunch of objects, including the custom ones - but if I try self.findChildren(MyWidget)
, an empty list is returned. Also, if you run self.dumpObjectTree()
, there are objects of class MyWidget
present in the output - so it is a bit strange for me, why cannot .findChildren
find them.
I have so far found this:
How to find an object by name in pyqt?
You can use QObject::findChild method.
So, this post notes, that even if looking up by name (.objectName()
), findChild
should be used.
You don't need to use findChild() since if you use loadUi or loadUiType it will map the objects using the objectName.
This refers to the OP problem in that post, so I could not tell if it is possible or not to use findChild()
or findChildren()
in such a case in principle.
In any case, I do not want to manually keep a list of names [self.my_widget_01, self.my_widget_02]
, because there may be dynamically added widgets in addition to those present in the .ui - so I'd really, really like to look them up by a name regex (for instance "my_widget_\d\d"
) - or by class (so I'd look up MyWidget
); it does not matter in this case, as I'd keep all MyWidget
widgets named as my_widget_XY
. I need this so that I could loop over them, regardless of how many they end up being in the GUI.
Is this (getting a list of all promoted MyWidget
widgets, regardless if they are present in the .ui file, or added dynamically) possible to do in PyQt5 - and if so, how?
test1.ui
:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>302</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QSplitter" name="splitter">
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>558</width>
<height>192</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListView" name="listView"/>
<widget class="QTreeView" name="treeView"/>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="MyWidget" name="my_widget_02">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="MyWidget" name="my_widget_01">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>MyWidget</class>
<extends>QFrame</extends>
<header>test1</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
test1.py
import sys
from PyQt5 import QtCore, QtWidgets, QtGui, uic
from PyQt5.QtCore import pyqtSlot
class MyWidget(QtWidgets.QFrame):
def __init__(self, *args, **kwargs):
super(MyWidget, self).__init__(*args, **kwargs)
self.hlay = QtWidgets.QHBoxLayout(self)
self.hlay.setContentsMargins(1, 1, 1, 1)
self.label = QtWidgets.QLabel(text="{}".format(self))
self.hlay.addWidget(self.label, 0, QtCore.Qt.AlignVCenter)
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
uic.loadUi('test1.ui', self)
self.dumpObjectTree() # debug, auto-prints to stdout
print("find QFrame:", self.findChildren(QtWidgets.QFrame)) # find QFrame: [<PyQt5.QtWidgets.QSplitter object at 0x00000000060f8a60>, <PyQt5.QtWidgets.QFrame object at 0x00000000060f8c10>, <test1.MyWidget object at 0x00000000060f8d30> ...
print("find MyWidget:", self.findChildren(MyWidget)) # []
self.show()
print("after show")
print("find MyWidget:", self.findChildren(MyWidget)) # []
QtCore.QTimer.singleShot(1, self.delayed_init) # run after 1 ms
def delayed_init(self):
print("delayed MyWidget:", self.findChildren(MyWidget)) # []
def main():
app = QtWidgets.QApplication(sys.argv)
window = MyMainWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Output in terminal:
$ python3 /tmp/test1.py
MyMainWindow::MainWindow
QMainWindowLayout::_layout
QWidget::centralwidget
QSplitter::splitter
QFrame::frame
QHBoxLayout::horizontalLayout
MyWidget::my_widget_02
QHBoxLayout::
QLabel::
MyWidget::my_widget_01
QHBoxLayout::
QLabel::
QTreeView::treeView
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QStyledItemDelegate::
QHeaderView::
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QListView::listView
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QStyledItemDelegate::
QSplitterHandle::qt_splithandle_
QSplitterHandle::qt_splithandle_
QSplitterHandle::qt_splithandle_
QMenuBar::menubar
QToolButton::qt_menubar_ext_button
QStatusBar::statusbar
QSizeGrip::
QHBoxLayout::
QVBoxLayout::
QHBoxLayout::
find QFrame: [<PyQt5.QtWidgets.QSplitter object at 0x00000000060f9af0>, <PyQt5.QtWidgets.QFrame object at 0x00000000060f9ca0>, <test1.MyWidget object at 0x00000000060f9dc0>, <PyQt5.QtWidgets.QLabel object at 0x0000000006684160>, <test1.MyWidget object at 0x00000000066841f0>, <PyQt5.QtWidgets.QLabel object at 0x0000000006684310>, <PyQt5.QtWidgets.QTreeView object at 0x00000000060f9c10>, <PyQt5.QtWidgets.QHeaderView object at 0x00000000066844c0>, <PyQt5.QtWidgets.QListView object at 0x00000000060f9b80>]
find MyWidget: []
after show
find MyWidget: []
delayed MyWidget: []