Toy MVC (Model View Controller) design

   1 ##Some points to mention...
   2 ##
   3 ##The model knows nothing about the view or the controller.
   4 ##The view knows nothing about the controller or the model.
   5 ##The controller understands both the model and the view.
   6 ##
   7 ##The model uses observables, essentially when important data is changed,
   8 ##any interested listener gets notified through a callback mechanism.
   9 ##
  10 ##The following opens up two windows, one that reports how much money you
  11 ##have, and one that has two buttons, one to add money and one to remove
  12 ##money.
  13 ##
  14 ##The important thing is that the controller is set up to monitor changes
  15 ##in the model.  In this case the controller notices that you clicked a
  16 ##button and modifies the money in the model which then sends out a
  17 ##message that it has changed.  The controller notices this and updates
  18 ##the widgets.
  19 ##
  20 ##The cool thing is that anything modifying the model will notify the
  21 ##controller.  In this case it is the controller modifying the model, but it
  22 ##could be anything else, even another controller off in the distance
  23 ##looking at something else.
  24 ##
  25 ##The main idea is that you give a controller the model and view that it
  26 ##needs, but the model's can be shared between controllers so that when
  27 ##the model is updated, all associated views are updated. -Brian Kelley
  28 ##
  29 ## following is a Tkinter approximation of the original example.
  30 
  31 import Tkinter as tk
  32 
  33 
  34 class Observable:
  35     def __init__(self, initialValue=None):
  36         self.data = initialValue
  37         self.callbacks = {}
  38 
  39     def addCallback(self, func):
  40         self.callbacks[func] = 1
  41 
  42     def delCallback(self, func):
  43         del self.callback[func]
  44 
  45     def _docallbacks(self):
  46         for func in self.callbacks:
  47              func(self.data)
  48 
  49     def set(self, data):
  50         self.data = data
  51         self._docallbacks()
  52 
  53     def get(self):
  54         return self.data
  55 
  56     def unset(self):
  57         self.data = None
  58 
  59 
  60 class Model:
  61     def __init__(self):
  62         self.myMoney = Observable(0)
  63 
  64     def addMoney(self, value):
  65         self.myMoney.set(self.myMoney.get() + value)
  66 
  67     def removeMoney(self, value):
  68         self.myMoney.set(self.myMoney.get() - value)
  69 
  70 
  71 class View(tk.Toplevel):
  72     def __init__(self, master):
  73         tk.Toplevel.__init__(self, master)
  74         self.protocol('WM_DELETE_WINDOW', self.master.destroy)
  75         tk.Label(self, text='My Money').pack(side='left')
  76         self.moneyCtrl = tk.Entry(self, width=8)
  77         self.moneyCtrl.pack(side='left')
  78         
  79     def SetMoney(self, money):
  80         self.moneyCtrl.delete(0,'end')
  81         self.moneyCtrl.insert('end', str(money))        
  82 
  83 
  84 class ChangerWidget(tk.Toplevel):
  85     def __init__(self, master):
  86         tk.Toplevel.__init__(self, master)
  87         self.addButton = tk.Button(self, text='Add', width=8)
  88         self.addButton.pack(side='left')
  89         self.removeButton = tk.Button(self, text='Remove', width=8)
  90         self.removeButton.pack(side='left')        
  91 
  92 
  93 class Controller:
  94     def __init__(self, root):
  95         self.model = Model()
  96         self.model.myMoney.addCallback(self.MoneyChanged)
  97         self.view1 = View(root)
  98         self.view2 = ChangerWidget(self.view1)
  99         self.view2.addButton.config(command=self.AddMoney)
 100         self.view2.removeButton.config(command=self.RemoveMoney)
 101         self.MoneyChanged(self.model.myMoney.get())
 102         
 103     def AddMoney(self):
 104         self.model.addMoney(10)
 105 
 106     def RemoveMoney(self):
 107         self.model.removeMoney(10)
 108 
 109     def MoneyChanged(self, money):
 110         self.view1.SetMoney(money)
 111 
 112 
 113 if __name__ == '__main__':
 114     root = tk.Tk()
 115     root.withdraw()
 116     app = Controller(root)
 117     root.mainloop()

tkinter: ToyMVC (last edited 2011-07-18 05:03:49 by AnthonyMuss)