在PyQt布局中循环遍历小部件

15

我的问题与在PyQt中获取布局的小部件有些相关,但它不是一个重复的问题。我不是在寻找高层次的战略视图来完成它,而是试图理解最惯用和直接的方法。由于PyQt是Qt C++ API的准确绑定,因此它提供了一种类似C语言的方法来访问布局中的小部件。以下是我一直在使用的惯用语:

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())

我并不是最有经验的Python程序员,但是这段代码看起来似乎不太符合Pythonic的风格。我的直觉告诉我,在理想情况下,Python代码应该更像这样:

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

我错了吗?我有没有错过更好的习语?这是在PyQt中迭代小部件的最佳方式吗?我倾向于用C++思考,因此有时会忽略使事情变得更好的“明显”的Python语言特性。我所做的一部分是递归地进入具有小部件布局的小部件,具有布局的小部件(等等...),以便自动连接在设计时制作的UI的连接。加入QTabWidgets和处理在设计师中设置的动态属性,我的代码基本上可以工作,但感觉非常笨重。


我只是想确保您知道可以直接从您的UI实例访问所有小部件。 您可以在UI实例上使用dir()枚举UI中的所有对象(但这也会给您带来像__class__和__eq__之类的垃圾,但这很容易被过滤掉)。 - goertzenator
5个回答

25

虽然回复有些晚,但我认为对于未来的参考可能仍有用处。我也在寻找这个问题的答案,但我想要识别小部件类型,以便能够相应地处理它们。这是我找到的示例代码:

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())

我希望有一天这对某个人会有所帮助。 :)


11

只是一条评论,

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

我尝试了第一个答案,但我发现它返回一个WidgetItem类型,所以实际上我进行了修订:


我尝试了第一个回答,但我发现它返回了一个WidgetItem类型,所以我进行了修改:
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())

10

您可以将小部件放入生成器中,如下所示:

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

如果你经常使用那段代码,你可以将它放到一个生成器函数中:

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


for w in layout_widgets(layout):
   doSomething(w)

1
请注意,这些“项”不是QWidgets!它们属于QLayoutItem类。您必须调用widget()才能访问QWidget。 - mike rodent

2

如果您可以访问WindowHandler,则可以通过以下简单操作获取所有对象的字典:

widgets = vars(self)

请注意,我正在使用QT Designer(5.12),并没有完整的示例展示我如何添加这样的小部件。然而,这是我的一个例子:
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())

结果是:

<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

0

我相信你已经有了解决这个问题的最简单方法。PyQt(和PySide)大多只是C++ API的包装器。它们在一些领域中添加了更多的Python支持,如信号/槽、基本类型(如QString)等,但在大多数情况下,一切都保持不变。

当然,这并不妨碍你自己创建更加Pythonic的库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接