Loop over widgets in PyQt Layout
Asked Answered
H

5

16

My question is somewhat related to Get a layout's widgets in PyQT but it's not a duplicate. Instead of looking for a high level strategic view of how to do it, I'm trying to understand what the most idiomatic and straightforward way to do it would be. Since PyQt is a pretty exact binding of the Qt C++ API, it presents a C-ish way to get at widgets in a layout. Here is the sort of idiom I have been using:

for i in range(layout.count()):
  item = layout.itemAt(i)
  if type(item) == QtGui.QLayoutItem:
    doSomeStuff(item.layout())
  if type(item) == QtGui.QWidgetItem:
doSomething(item.widget())

I'm not the most experienced Python guy, but this seems somewhat unpythonic. My intuition is telling me that in an ideal world, the Python code should look something more like:

for w in layout.widgets():
  doSomething(w)

Am I wrong? Am I missing a superior idiom? Is this the best possible way to iterate over widgets in PyQt? I tend to think in C++, so I sometimes miss out on "obvious" Python language features that make things better. Part of what I'm doing is recursively descending into widgets with layouts with widgets with layouts (etc...) to automatically wire up connections to UI's made in Designer at run time. Add in QTabWidgets, and handling of the dynamic properties set in designer, and my code basically works, but it just feels horribly clunky.

Hoyt answered 1/3, 2011 at 3:29 Comment(1)
I just want to make sure that you know you can get to all widgets directly from your ui instance. You can use dir() on your ui instance to enumerate all the objects in your ui (but this also gives you cruft like class and eq, but that is easily filtered out).Hennessey
H
10

You could put the widgets into a generator like so:

items = (layout.itemAt(i) for i in range(layout.count())) 
for w in items:
   doSomething(w)

If you end up using that a lot, you could sock that code into a generator function:

def layout_widgets(layout):
   return (layout.itemAt(i) for i in range(layout.count()))


for w in layout_widgets(layout):
   doSomething(w)
Hennessey answered 4/3, 2011 at 15:29 Comment(1)
Note that these "items" are not QWidgets! They are of class QLayoutItem. You have to call widget() on them to get to the QWidget.Idola
N
28

This is a very late response but I thought it might be useful for future refence. I was looking for the answer to this question also. But I wanted to identify widget types so I could handle them accordingly. Here is example code of what I found:

for widget in centralwidget.children():
    if isinstance(widget, QLineEdit):
        print "linedit: %s  - %s" %(widget.objectName(),widget.text())

    if isinstance(widget, QCheckBox):
        print "checkBox: %s  - %s" %(widget.objectName(),widget.checkState())

I hope that will be useful for someone someday. :)

Nazarene answered 22/3, 2013 at 20:1 Comment(0)
H
12

Just a comment,

items = (layout.itemAt(i) for i in range(layout.count())) 
for w in items:
   doSomething(w)

I tried the first answer but I found that it returns a WidgetItem type, so actually I did a revision:

widgets = (layout.itemAt(i).widget() for i in range(layout.count())) 
for widget in widgets:
   if isinstance(widget, QLineEdit):
        print "linedit: %s  - %s" %(widget.objectName(), widget.text())
   if isinstance(widget, QCheckBox):
        print "checkBox: %s  - %s" %(widget.objectName(), widget.checkState())
Hurds answered 22/11, 2014 at 3:45 Comment(0)
H
10

You could put the widgets into a generator like so:

items = (layout.itemAt(i) for i in range(layout.count())) 
for w in items:
   doSomething(w)

If you end up using that a lot, you could sock that code into a generator function:

def layout_widgets(layout):
   return (layout.itemAt(i) for i in range(layout.count()))


for w in layout_widgets(layout):
   doSomething(w)
Hennessey answered 4/3, 2011 at 15:29 Comment(1)
Note that these "items" are not QWidgets! They are of class QLayoutItem. You have to call widget() on them to get to the QWidget.Idola
O
2

Found a way if you can access the WindowHandler then you can get a dictionary of all the objects by simply doing:

widgets = vars(self)

Please note that I am using QT Designer (5.12) and don't have a full example showing how I added such widgets However, here is my example:

from PyQt5.QtWidgets import *
FORM_CLASS, _ = loadUiType(path.join(path.dirname('__file__'), "main.ui"))

class HMIWindowHandler(QMainWindow, FORM_CLASS):
    def __init__(self, parent=None):
        super(HMIWindowHandler, self).__init__(parent)
        QMainWindow.__init__(self)
        self.setupUi(self)

        # Prints out self to prove that it is a Window Handler.
        print(self)
        print(vars(self))

        # The vars function will return all of the items of the window handler as a dict.
        widgets = vars(self)

        # Prints the dict to prove class.
        print(type(widgets))

        # Iterates each key checks for the class type and then prints the objectsName
        for each_key, value_of_key in widgets.items():
            if (value_of_key.__class__.__name__ == 'QPushButton' or value_of_key.__class__.__name__ == 'QLabel'):
                print(value_of_key.objectName())

The result is:

<views.admin.HMIWindowHandler object at 0x0000017BAC5D9EE0>

dict_items([('centralwidget', <PyQt5.QtWidgets.QWidget object at 0x0000017BAC5D9F70>),
('windows_stacked', <PyQt5.QtWidgets.QStackedWidget object at 0x0000017BAC79A040>),
('brewing', <PyQt5.QtWidgets.QWidget object at 0x0000017BAC79A0D0>), 
('boil_image', <PyQt5.QtWidgets.QLabel object at 0x0000017BAC79A160>)])

<class 'dict'>

boil_image
mash_image
Outpouring answered 15/2, 2021 at 4:36 Comment(0)
B
0

I believe you already have the simplest solution to this problem. PyQt (and PySide) are mostly just wrappers around the C++ API. They do add more pythonic support in a few areas--signals/slots, basic types such as QString, etc. but for the most part, everything is left exactly as it is in the C++ API.

Of course, this need not stop you from making your own more pythonic library..

Butyraceous answered 3/3, 2011 at 23:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.