【问题标题】:Loop over widgets in PyQt Layout在 PyQt 布局中循环小部件
【发布时间】:2011-03-01 03:29:27
【问题描述】:

我的问题与Get a layout's widgets in 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 人,但这似乎有点不合 Python。我的直觉告诉我,在理想世界中,Python 代码应该看起来更像:

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

我错了吗?我错过了一个高级成语吗?这是在 PyQt 中迭代小部件的最佳方法吗?我倾向于用 C++ 来思考,所以我有时会错过让事情变得更好的“显而易见”的 Python 语言特性。我正在做的部分工作是递归地下降到带有布局的小部件(带有带有布局的小部件(等等)),以在运行时自动连接到在 Designer 中制作的 UI。添加QTabWidgets,处理设计器中设置的动态属性,我的代码基本可以工作了,但是感觉很笨拙。

【问题讨论】:

  • 我只是想确保您知道您可以直接从您的 ui 实例访问所有小部件。您可以在您的 ui 实例上使用 dir() 来枚举您的 ui 中的所有对象(但这也会给您带来像 classeq 这样的笨拙,但这很容易被过滤掉)。

标签: python pyqt


【解决方案1】:

这是一个很晚的回应,但我认为它可能对未来的参考有用。我也在寻找这个问题的答案。但我想识别小部件类型,以便我可以相应地处理它们。这是我发现的示例代码:

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

我希望有一天这对某人有用。 :)

【讨论】:

    【解决方案2】:

    只是评论,

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

    我尝试了第一个答案,但我发现它返回一个 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())
    

    【讨论】:

      【解决方案3】:

      您可以像这样将小部件放入生成器中:

      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)
      

      【讨论】:

      • 请注意,这些“项目”不是 QWidgets!它们属于 QLayoutItem 类。你必须给他们打电话 widget() 才能访问 QWidget。
      【解决方案4】:

      找到一种方法,如果您可以访问 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
      

      【讨论】:

        【解决方案5】:

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

        当然,这并不需要阻止您制作自己的更 Python 的库..

        【讨论】: