Nobody said:
I'm trying to wrap my mind around MVC. Does anyone have a _simple_ example
of using the MVC pattern with wxPython? Maybe just a simple window with a
dialog box that allows one to update some data.
All right, here you go. Some points to mention...
The model knows nothing about the view or the controller.
The view knows nothing about the controller or the model.
The controller understands both the model and the view.
The model uses observables, essentially when important data is changed,
any interested listener gets notified through a callback mechanism.
The following opens up two windows, one that reports how much money you
have, and one that has two buttons, one to add money and one to remove
money.
The important thing is that the controller is set up to monitor changes
in the model. In this case the controller notices that you clicked a
button and modifies the money in the model which then sends out a
message that it has changed. The controller notices this and updates
the widgets.
The cool thing is that anything modifying the model will notify the
model. In this case it is the controller modifying the model, but it
could be anything else, even another controller off in the distance
looking at something else.
The main idea is that you give a controller the model and view that it
needs, but the model's can be shared between controllers so that when
the model is updated, all associated views are updated.
I sure hope that made sense, anyway, here is the code, enjoy.
import wx
# an observable calls callback functions when the data has
# changed
#o = Observable()
#def func(data):
# print "hello", data
#o.addCallback(func)
#o.set(1)
# --> "hello", 1
class Observable:
def __init__(self, initialValue=None):
self.data = initialValue
self.callbacks = {}
def addCallback(self, func):
self.callbacks[func] = 1
def delCallback(self, func):
del self.callback[func]
def _docallbacks(self):
for func in self.callbacks:
func(self.data)
def set(self, data):
self.data = data
self._docallbacks()
def get(self):
return self.data
def unset(self):
self.data = None
class Model:
def __init__(self):
self.myMoney = Observable(0)
def addMoney(self, value):
self.myMoney.set(self.myMoney.get() + value)
def removeMoney(self, value):
self.myMoney.set(self.myMoney.get() - value)
class View(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Main View")
sizer = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, -1, "My Money")
ctrl = wx.TextCtrl(self, -1, "")
sizer.Add(text, 0, wx.EXPAND|wx.ALL)
sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL)
self.moneyCtrl = ctrl
ctrl.SetEditable(False)
self.SetSizer(sizer)
self.moneyCtrl = ctrl
def SetMoney(self, money):
self.moneyCtrl.SetValue(str(money))
class ChangerWidget(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Main View")
sizer = wx.BoxSizer(wx.VERTICAL)
self.add = wx.Button(self, -1, "Add Money")
self.remove = wx.Button(self, -1, "Remove Money")
sizer.Add(self.add, 0, wx.EXPAND|wx.ALL)
sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL)
self.SetSizer(sizer)
class Controller:
def __init__(self, app):
self.model = Model()
self.view1 = View(None)
self.view2 = ChangerWidget(self.view1)
self.MoneyChanged(self.model.myMoney.get())
self.view2.add.Bind(wx.EVT_BUTTON, self.AddMoney)
self.view2.remove.Bind(wx.EVT_BUTTON, self.RemoveMoney)
self.model.myMoney.addCallback(self.MoneyChanged)
self.view1.Show()
self.view2.Show()
def AddMoney(self, evt):
self.model.addMoney(10)
def RemoveMoney(self, evt):
self.model.removeMoney(10)
def MoneyChanged(self, money):
self.view1.SetMoney(money)
app = wx.PySimpleApp()
Controller(app)
app.MainLoop()