对于一般的 GUI 工作:
少即是多
GUI 工作(即使在生产性框架中)与用牙刷绘制埃菲尔铁塔一样有趣且富有成效。寻求最小的设计。
避免像瘟疫一样的状态
您将状态放在 GUI 中还是模型中?如果你把它放在 GUI 中,你会用冗余和不一致的代码路径搞砸自己。如果将其放入模型中,则当您的 GUI 无法从模型更新时,您将面临一个过于复杂的系统失去同步的风险。两个都很烂。
wxPython
如果你想学习 wxPython,以下是我注意到的几个陷阱:
教程
使用本教程 - http://wiki.wxpython.org/AnotherTutorial
这是我找到的最好的。
但请记住切换行号,以便于粘贴。
活动
事件有点像异常,它们被用来使事物交互。
在一个普通的 Python 程序中,你可以编写如下代码:
def doit(i):
print 'Doing i = ',i
for i in range(10):
doit()
print 'Results = ',result
在 GUI 中,您可以执行以下操作:
def doit(event):
print 'An event',event,'just happened!'
event.Skip()
import wx
app = wx.App()
frame = wx.Frame(None, -1, 'The title goes here')
frame.Bind(wx.EVT_KEY_DOWN, doit)
frame.Show()
app.MainLoop()
每次用户按下一个键时,都会引发一个事件。由于frame 绑定到事件(frame.Bind(wx.EVT_KEY_DOWN, doit)),函数doit 将作为参数调用。
在 gui 中打印到 stderr 并不太热,但是 doit 也可以调用一个对话框,或者做任何你想做的事情。
此外,您可以使用计时器生成自己的事件。
应用程序、框架、窗口、面板和 Sizer
万物皆有父。如果引发了一个事件,并且子级没有跳过它(使用event.Skip()),那么父级也必须处理该事件。这类似于引发更高级别函数的异常。
wx.App 类似于 Main 函数。
wx.Window 并没有真正使用。 Stuff 继承自它,它具有所有大小和布局的方法,但您不需要知道。
wx.Frame 是一个浮动框架,类似于 Firefox 中的主窗口。您将在基本应用程序中拥有主要的一帧。如果你想编辑多个文件,那么你可能有更多。 wx.Frame 通常没有父级。
wx.Panel 是父窗口的一部分。一个框架内可以有多个面板。一个面板可以有一个wx.Frame 作为父面板,或者它可能是另一个面板的子面板。
wx.Sizers 用于在框架(或其他面板)内自动布局面板。
代码:
def doit1(event):
print 'event 1 happened'
def doit2(event):
print 'event 2 happened'
import wx
app = wx.App()
frame = wx.Frame(None, -1, 'The title goes here')
panel_1 = wx.Panel(frame,-1,style=wx.SIMPLE_BORDER)
panel_2 = wx.Panel(frame,-1,style=wx.SIMPLE_BORDER)
panel_1.Bind(wx.EVT_KEY_DOWN, doit1)
panel_2.Bind(wx.EVT_KEY_DOWN, doit2)
panel_1.SetBackgroundColour(wx.BLACK)
panel_2.SetBackgroundColour(wx.RED)
box = wx.BoxSizer(wx.HORIZONTAL)
box.Add(panel_1,1,wx.EXPAND)
box.Add(panel_2,1,wx.EXPAND)
frame.SetSizer(box)
frame.Show()
app.MainLoop()
我真的很糟糕,没有使用 OOP 实践。请记住,即使您在大多数情况下都讨厌 OO,但 GUI 编程才是 OOP 真正闪耀的地方。
MCV
我没有获得 MCV。我不认为你需要一个 MCV。我认为 MW(模型小部件)框架很好。
例如 - 编辑同一段文本的 2 个框架:
class Model(object):
def __init__(self):
self.value = 'Enter a value'
self.listeners = []
def Add_listener(self,listener):
self.listeners.append(listener)
def Set(self,new_value):
self.value = new_value
for listener in self.listeners:
listener.Update(self.value)
import wx
app = wx.App()
class CVFrame(wx.Frame):
def __init__(self, parent, id, title, model):
wx.Frame.__init__(self, parent, id, title, size = (100,100))
self.button = wx.Button(self, -1, 'Set model value')
self.textctrl = wx.TextCtrl(self, -1,model.value)
self.button.Bind(wx.EVT_BUTTON,self.OnSet)
self.model = model
model.Add_listener(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.button,0,wx.EXPAND)
sizer.Add(self.textctrl,1,wx.EXPAND)
self.SetSize((300,100))
self.SetSizer(sizer)
self.Center()
self.Show()
def OnSet(self,event):
self.model.Set(self.textctrl.GetValue())
def Update(self,value):
self.textctrl.SetValue(value)
model = Model()
frame1 = CVFrame(None, -1, 'Frame 1',model)
frame2 = CVFrame(None, -1, 'Frame 2',model)
app.MainLoop()
wxPython 有一个 listener-subscriber 框架,它是我刚刚勾勒出的模型的一个更好的版本(它使用弱引用,所以被删除的侦听器不会闲逛,等等),但这应该可以帮助你获得想法。