2

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

I would like to collect user input, but the input field differs through 'method' change, so i would like to generate something based on:

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

serves as dynamic input field

the expected result is that the key generates as radio button, and will generate ['label', 'line edit'] pairs to certain radio selection

here is the test of exec inside pyqt (clearLayout borrowed from user3369214

steps:

  1. create widgets
  2. create function that add them to layouts
  3. connect signal to function that change the layout

the advantage compared to add them one by one is that this is flexible to extend and convenient to collect data back,(most importantly, short), but program happens to say

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

there might be some scope or internal problem, hope someone could help or some high-order function thing might help?

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

I've also test in normal class, but that works fine ( could recognize the '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()'
Community
  • 1
  • 1
Jack Wu
  • 447
  • 4
  • 14
  • 1
    You need to pass in the dictionary of global and local variables in order for the `exec` to operate in the correct namespace. So the correct usage would be `exec('string to exec', globals(), locals())`. However you should really just use the dictionary option that is in the answer below. `exec` should be avoided if possible. – three_pineapples Mar 15 '16 at 09:49
  • yes, concating exec is rude, thx for your kind advice :) – Jack Wu Mar 15 '16 at 12:40

1 Answers1

1

exec is not necessary. It make the code harder to read. How about using a dictionaries to hold widgets?

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_())
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • thanks a lot, I also tried to use some dictionary to hold the widgets later on, but not sure how to arrange and generate. you did solve my question, Thx :) – Jack Wu Mar 15 '16 at 12:41