Python& wxPython - 代码批判 - 短手/便利/重复

时间:2010-11-09 16:10:21

标签: python layout automation wxpython repeat

我实际上想首先承认我很害怕问这个问题。也就是说,我有以下几个类的组合:

对话类:

class formDialog(wx.Dialog):
  def __init__(self, parent, id = -1, panel = None, title = _("Unnamed Dialog"),
               modal = False, sizes = (400, -1)):
    wx.Dialog.__init__(self, parent, id, _(title),
                       style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)

    if panel is not None:
      self._panel = panel(self)

      self._panel.SetSizeHints(*sizes)

      ds = wx.GridBagSizer(self._panel._gap, self._panel._gap)

      ds.Add(self._panel, (0, 0), (1, 1), wx.EXPAND | wx.ALL, self._panel._gap)

      ds.Add(wx.StaticLine(self), (1, 0), (1, 1), wx.EXPAND | wx.RIGHT | wx.LEFT, self._panel._gap)

      self.bs = self.CreateButtonSizer(self._panel._form['Buttons'])

      ds.Add(self.bs, (2, 0), (1, 1), wx.ALIGN_RIGHT | wx.ALL, self._panel._gap)

      ds.AddGrowableCol(0)
      ds.AddGrowableRow(0)

      self.SetSizerAndFit(ds)

      self.Center()

      self.Bind(wx.EVT_BUTTON, self._panel.onOk, id = wx.ID_OK)
      self.Bind(wx.EVT_BUTTON, self._panel.onClose, id = wx.ID_CANCEL)
      self.Bind(wx.EVT_CLOSE, self._panel.onClose)

      if modal:
        self.ShowModal()
      else:
        self.Show()

表格类:

class Form(wx.Panel):
  reqFields = [
    ('Defaults', {}),
    ('Disabled', [])
  ]

  def __init__(self, parent = None, id = -1, gap = 2, sizes = (-1, -1)):
    wx.Panel.__init__(self, parent, id)

    self.SetSizeHints(*sizes)

    self._gap = gap

    self.itemMap = {}

    if hasattr(self, '_form'):
      # There are a number of fields which need to exist in the form
      # dictionary.  Set them to defaults if they don't exist already.
      for k, d in self.reqFields:
        if not self._form.has_key(k):
          self._form[k] = d

      self._build()

  def _build(self):
    """
    The Build Method automates sizer creation and element placement by parsing
    a properly constructed object.
    """

    # The Main Sizer for the Panel.
    panelSizer = wx.GridBagSizer(self._gap, self._gap)

    # Parts is an Ordered Dictionary of regions for the form.
    for group, (key, data) in enumerate(self._form['Parts'].iteritems()):
      flags, sep, display = key.rpartition('-') #@UnusedVariable

      # HR signifies a Horizontal Rule for spacing / layout.  No Data Field.
      if display == 'HR':
        element = wx.StaticLine(self)

        style = wx.EXPAND

      # Any other value contains elements that need to be placed.
      else:
        element = wx.Panel(self, -1)

        # The Row Sizer
        rowSizer = wx.GridBagSizer(self._gap, self._gap)

        for row, field in enumerate(data):
          for col, item in enumerate(field):
            style = wx.EXPAND | wx.ALL

            pieces = item.split('-')

            # b for Buttons
            if pieces[0] == 'b':
              control = wx._controls.Button(element, -1, pieces[1])

            # custom items - Retrieve from the _form object
            if pieces[0] == 'custom':
              control = self._form[pieces[1]](element)

              # The row in the Grid needs to resize for Lists.
              panelSizer.AddGrowableRow(group)

              # Now the Row has to grow with the List as well.
              rowSizer.AddGrowableRow(row)

            # custom2 - Same as custom, but does not expand
            if pieces[0] == 'custom2':
              control = self._form[pieces[1]](element)

              style = wx.ALL

            # c for CheckBox
            if pieces[0] == 'c':
              control = wx.CheckBox(element, label = _(pieces[2]), name = pieces[1])

              control.SetValue(int(self._form['Defaults'].get(pieces[1], 0)))

            # d for Directory Picker
            if pieces[0] == 'd':
              control = wx.DirPickerCtrl(element, name = pieces[1])

              control.GetTextCtrl().SetEditable(False)

              control.GetTextCtrl().SetName(pieces[1])

              control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))

            # f for File Browser
            if pieces[0] == 'f':
              control = wx.FilePickerCtrl(element, name = pieces[1], wildcard = pieces[2])

              control.GetTextCtrl().SetEditable(False)

              control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))

            # f2 for Save File
            if pieces[0] == 'f2':
              control = wx.FilePickerCtrl(element, name = pieces[1],
                style = wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
                wildcard = pieces[2])

              control.GetTextCtrl().SetEditable(False)

            # h for Horizontal Rule - layout helper.
            if pieces[0] == 'h':
              control = wx.StaticLine(element)
              style = wx.EXPAND

            # l for Label (StaticText)
            if pieces[0] == 'l':
              control = wx.StaticText(element, label = _(pieces[1]))

              # Labels do not expand - override default style.
              style = wx.ALL | wx.ALIGN_CENTER_VERTICAL

            # p for Password (TextCtrl with Style)
            if pieces[0] == 'p':
              control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_PASSWORD)

              control.SetValue(self._form['Defaults'].get(pieces[1], ''))

            # s for ComboBox (Select)
            if pieces[0] == 's':
              control = wx.ComboBox(element, name = pieces[1],
                choices = self._form['Options'].get(pieces[1], []),
                style = wx.CB_READONLY)

              control.SetValue(self._form['Defaults'].get(pieces[1], ''))

            # s2 for Spin Control
            if pieces[0] == 's2':
              control = wx.SpinCtrl(element, name = pieces[1], size = (55, -1),
                min = int(pieces[2]), max = int(pieces[3]))

              control.SetValue(int(self._form['Defaults'].get(pieces[1], 1)))

              # Spin Ctrl's do not expand.
              style = wx.ALL

            # t for TextCtrl
            if pieces[0] == 't':
              control = wx.TextCtrl(element, name = pieces[1])

              try:
                control.SetValidator(self._form['Validators'][pieces[1]])
              except KeyError: pass # No Validator Specified.

              control.SetValue(self._form['Defaults'].get(pieces[1], ''))

            # tr for Readonly TextCtrl
            if pieces[0] == 'tr':
              control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_READONLY)

              control.SetValue(self._form['Defaults'].get(pieces[1], ''))

            # Check for elements disabled by default.  Store reference to 
            # Element in itemMap for reference by other objects later.
            if len(pieces) > 1:
              if pieces[1] in self._form['Disabled']:
                control.Enable(False)

              self.itemMap[pieces[1]] = control

            # Place the control in the row.
            rowSizer.Add(control, (row, col), (1, 1), style, self._gap)

            if style == wx.EXPAND | wx.ALL:
              rowSizer.AddGrowableCol(col)

        if 'NC' not in flags:
          sb = wx.StaticBox(element, -1, _(display))
          sz = wx.StaticBoxSizer(sb, wx.VERTICAL)

          sz.Add(rowSizer, 1, flag = wx.EXPAND)

          element.SetSizerAndFit(sz)
        else:
          element.SetSizerAndFit(rowSizer)

      panelSizer.Add(element, (group, 0), (1, 1), wx.EXPAND | wx.ALL, self._gap)

    panelSizer.AddGrowableCol(0)

    self.SetSizerAndFit(panelSizer)

  def getDescendants(self, elem, list):
    children = elem.GetChildren()

    list.extend(children)

    for child in children:
      self.getDescendants(child, list)

  def getFields(self):
    fields = []

    self.getDescendants(self, fields)

    # This removes children we can't retrieve values from.  This should result
    # in a list that only contains form fields, removing all container elements.
    fields = filter(lambda x: hasattr(x, 'GetValue'), fields)

    return fields

  def onOk(self, evt):
    self.onClose(evt)

  def onClose(self, evt):
    self.GetParent().Destroy()

表单用于子类化,如下所示:

class createQueue(Form):
  def __init__(self, parent):
    self._form = {
      'Parts' : OrderedDict([
        ('Queue Name', [
          ('t-Queue Name',)
        ])
      ]),
      'Buttons' : wx.OK | wx.CANCEL
    }

    Form.__init__(self, parent)

class generalSettings(Form):
  def __init__(self, parent):
    self._form = {
      'Parts': OrderedDict([
        ('Log Settings', [
          ('l-Remove log messages older than: ', 's2-interval-1-10', 's-unit')
        ]),
        ('Folder Settings', [
          ('l-Spool Folder Location:', 'd-dir'),
          ('l-Temp Folder Location:', 'd-temp')
        ]),
        ('Email Notifications', [
          ('l-Alert Email To:', 't-alert_to'),
          ('l-Alert Email From:', 't-alert_from'),
          ('l-Status Email From:', 't-status_from'),
          ('l-Alert Email Server:', 't-alert_host'),
          ('l-Login:', 't-alert_login'),
          ('l-Password:', 'p-alert_password')
        ]),
        ('Admin User', [
          ('c-req_admin-Require Admin Rights to make changes.',)
        ]),
        ('Miscellaneous', [
          ('l-Print Worker Tasks:', 's2-printtasks-1-256', 'l-Job Drag Options:', 's-jobdrop')
        ])
      ]),
      'Options': {
        'unit': ['Hours', 'Days', 'Months'],
        'jobdrop': ['Move Job to Queue', 'Copy Job to Queue']
      },
      'Buttons': wx.OK | wx.CANCEL
    }

    Form.__init__(self, parent)

这些可能会像这样使用:

formDialog(parent, panel = createQueue, title = 'Create a Queue', sizes = (200, -1))

formDialog(parent, panel = generalSettings, title = "General Settings")

哇,这太棒了,感谢任何一个让人失望的人。我的想法是,我想要一些能够处理wxPython中单调布局的东西。我正在设计一个用户界面,需要创建100个不同的对话框和表单。我想要一些允许我从结构化对象动态生成表单的东西。

我想听听其他开发者对这种方法的看法。我见过的最接近类似的东西是Drupal的Form API。我觉得这样做是可行的:

  • 轻松重新排列字段。
  • 无需手动创建/管理Sizer。
  • 可以轻松创建复合/复合形式。
  • 可以轻松添加显示辅助元素(StaticBoxSizers,Static Lines)。

我担心由于以下原因这是一种不受欢迎的方法:

  • 表单类中的长_build()函数体。
  • 乍看之下其他开发人员可能并不清楚。
  • 使用结构化字符串定义字段。
  • 可能有更好的方法。

任何想法,建设性,破坏性或其他方面都将受到赞赏。谢谢!

2 个答案:

答案 0 :(得分:2)

您还应该尝试使用wxFormDesigner或XRCed。

答案 1 :(得分:0)

由于你正在使用wx,你应该学习wxglade。它是一个图形化的GUI构建器,您可以使用它构建GUI,并使用布局生成.wxg文件,您可以将其加载到脚本中。

该文件实际上只是xml,因此您可以以编程方式生成它并从中动态加载不同的GUI。也许这有帮助。