使用 exec 生成函数以将小部件放置在 pyqt 中无法识别自身

generate functions to place widgets in pyqt using exec did not recognize self

refers this question, to-convert-string-to-variable-name-in-python

我想收集用户输入,但输入字段因 'method' 变化而不同,所以我想生成一些基于:

search_method = { 'id' : ['id'],
                  'detail' : ['catagory', 'price', 'enroll date'],
                  'drawer' : ['name', 'sex', 'state']
                  'internal' : ['transaction date', 'msg id']
                }

用作动态输入字段

预期结果是key生成为radio button,并且会生成['label', 'line edit'] pairs to certain radio selection

这里是pyqt里面exec的测试 (clearLayout 借自 user3369214

步骤:

  1. 创建小部件
  2. 创建将它们添加到布局的函数
  3. 将信号连接到更改布局的函数

相比一个一个加起来的优点是扩展灵活,回传数据方便,(最重要的是短),但是程序恰好说

NameError: global name 'self' is not defined
or SyntaxError: can't assign to function call

可能存在一些范围或内部问题,希望有人能提供帮助 或者一些高阶函数可能有帮助?

from PyQt4 import QtCore, QtGui
import sys

class TestDialog(QtGui.QDialog):
    def __init__(self, parent = None):
        super(TestDialog, self).__init__()
        self.initUI()

    def clearLayout(self, layout)
        if layout != None:
            while layout.count():
                child = layout.takeAt(0)
                if child.widget() is not None:
                    child.widget().deleteLater()
                elif child.layout() is not None:
                    self.clearLayout(child.layout())

        layout.setParent(None)

    def initUI(self):
        search_method = { 'id' : ['id'],
                          'detail' : ['catagory', 'price', 'enroll date'],
                          'drawer' : ['name', 'sex', 'state']
                          'internal' : ['transaction date', 'msg id']
                        }

        self.layTop = QtGui.QHBoxLayout()
        self.lblBy = QtGui.QLabel("By")
        self.layTop.addWidget(self.lblBy)

        for option in search_method.keys():
            exec('self.rad_' + option + ' = QtGui.QRadioButton("' + option + '")')
            exec('self.layTop.addWidget(self.rad_' + option + ')')

        self.vlay = QtGui.QHBoxLayout()
        self.vlay.addLayout(self.layTop)
        self.layInput = QtGui.QVBoxLayout()

        for option in search_method.keys():
            code =  'def by_' + option + '():'

            code += 'self.clearLayout(self.layInput)'

            for input_field in search_method[option]:
                code +=  'self.lay_' + input_field + ' = QtGui.QHBoxLayout()'
                code += ';self.lbl_' + input_field + ' = QtGui.QLabel("' + input_field + '")'
                code += ';self.edit_' + input_field + ' = QtGui.QLineEdit()'

                code += ';self.lay_' + input_field + '.addWidget(self.lbl_' + input_field + ')'
                code += ';self.lay_' + input_field + '.addWidget(self.edit_' + input_field + ')'
                code += ';self.layInput.addLayout(self.lay_' + input_field + ')'

            exec code

        for option in options.keys():
            exec('self.rad_' + option + '.toggled.connect(by_' + option + ')')

        self.setLayout(self.vlay)

app = QtGui.QApplication(sys.argv)
testDialog = TestDialog()
testDialog.show()
sys.exit(testDialog.exec_())

我也在正常 class 中测试过,但效果很好(可以识别 'self')

class A:
    def __init__(self, parm1, parm2):
        self.parm1 = parm1
        self.parm2 = parm2

    def display(self):
        to_exec = ''
        to_exec += 'def jack():'
        to_exec += 'print "hey hey hey"'
        exec(to_exec)

        exec('print self.parm' + '1')
        exec('print self.parm' + '2')
        exec('jack()')

    def aha(self):
        exec(self.display()')

a = A('hello', 'world')
exec 'a.aha()'

exec 没有必要。它使代码更难阅读。使用字典来保存小部件怎么样?

import sys

from PyQt4 import QtGui


class TestDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__(parent)
        self.initUI()

    def clearLayout(self, layout):
        if layout is None:
            return
        while layout.count():
            child = layout.takeAt(0)
            if child.widget() is not None:
                child.widget().deleteLater()
            elif child.layout() is not None:
                self.clearLayout(child.layout())

        layout.setParent(None)

    def initUI(self):
        search_method = {
            'id': ['id'],
            'detail': ['catagory', 'price', 'enroll_date'],
            'drawer': ['name', 'sex', 'state'],
            'internal': ['transaction_date', 'msg_id'],
        }

        self.layTop = QtGui.QHBoxLayout()
        self.lblBy = QtGui.QLabel("By")
        self.layTop.addWidget(self.lblBy)
        self.radios = {}
        self.layouts = {}
        self.labels = {}
        self.edits = {}

        for option in search_method:
            r = self.radios[option] = QtGui.QRadioButton(option)
            self.layTop.addWidget(r)

        self.vlay = QtGui.QHBoxLayout()
        self.vlay.addLayout(self.layTop)
        self.layInput = QtGui.QVBoxLayout()

        def by_option(option):
            self.clearLayout(self.layInput)
            for input_field in search_method[option]:
                lbl = self.labels[input_field] = QtGui.QLabel(input_field)
                edit = self.edits[input_field] = QtGui.QLineEdit()
                layout = self.layouts[input_field] = QtGui.QHBoxLayout()
                layout.addWidget(lbl)
                layout.addWidget(edit)
                self.layInput.addLayout(layout)
            self.vlay.addLayout(self.layInput)

        for option in search_method:
            self.radios[option].toggled.connect(
                lambda yesno, option=option: by_option(option)
            )

        self.setLayout(self.vlay)


app = QtGui.QApplication(sys.argv)
testDialog = TestDialog()
testDialog.show()
sys.exit(testDialog.exec_())