Python与wxPython - 代码点评 - 简洁性/便利性/重复性
其实我想先说,我很害怕问这个问题。不过,我有以下这些类的组合:
一个对话框类:
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中布局繁琐部分的东西。我正在设计一个用户界面,需要创建数百个不同的对话框和表单。我希望有一个可以从结构化对象动态生成表单的方式。
我想听听其他开发者对这种方法的看法。我见过的最接近的类似东西是Drupal的表单API。我觉得这个方法可行的原因有:
- 可以轻松调整字段的位置。
- 不需要手动创建或管理布局。
- 可以轻松创建复合或复杂的表单。
- 可以轻松添加显示辅助元素(比如静态框和静态线)。
但我也担心这可能不是一个好的方法,原因有:
- 表单类中的
_build()
函数体很长。 - 其他开发者一开始可能不太明白。
- 使用结构化字符串来定义字段。
- 可能有更好的方法。
任何想法,无论是建设性的、破坏性的,或者其他的,我都很感激。谢谢!
2 个回答
0
既然你在用wx这个工具,那你应该了解一下wxglade。它是一个图形化的界面构建工具,你可以用它来设计你的界面,然后它会生成一个 .wxg
文件,这个文件里包含了你的界面布局,你可以把它加载到你的代码里。
这个文件其实就是一种xml格式,所以你也可以通过编程的方式来生成它,并且可以动态地从中加载不同的界面。希望这对你有帮助。
2
你也可以试试 wxFormDesigner 或者 XRCed 这两个工具。