Model-GUI-Mediator

One problem with Model-View-Adapter is that it assumes Views are derived
classes, each implementing specific behavior.

In the previous example, each
View performed a specific transformation to the data before displaying: the
Dial left it as is, while the Slider divided it by 1000.

In the Model-GUI-Mediator, the desire is not to subclass the toolkit’s widgets,
because it generally leads to proliferation of View classes. Instead, widgets
are used as they are, off-the-shelf from the toolkit. The obvious consequence
is that logic that is pertinent to the conversion of data for visualization
must go somewhere else. The Controller seems the obvious choice, however
keeping the same design as in MVA would be cumbersome: the single Controller
would have to differentiate the Views, and submit properly transformed data to
each View. A better solution is to have different Controllers, one per each
View, doing the relevant transformation. The code would therefore be like the
following: The View being an off-the-shelf component means it does not know
anything about the Controller. All the signal setup is done by the individual
Controllers. Also, off-the-shelf classes are not implementing the Observer
pattern

  1. class DialController(object):
  2. def __init__(self):
  3. self._view = None
  4. self._model = None
  5. def setModel(self, model):
  6. self._model = model
  7. self._model.register(self)
  8. def setView(self, view):
  9. self._view = view
  10. self._view.setRange(0,10000)
  11. self._view.connect(self._view,
  12. QtCore.SIGNAL("valueChanged(int)"),
  13. self.changeRpm)
  14. def changeRpm(self, rpm):
  15. if self._model:
  16. self._model.setRpm(rpm)
  17. def notify(self):
  18. if self._view:
  19. self._view.setValue(self._model.rpm())

And for the Slider it would be

  1. class SliderController(object):
  2. def __init__(self):
  3. self._view = None
  4. self._model = None
  5. def setModel(self, model):
  6. self._model = model
  7. self._model.register(self)
  8. def setView(self, view):
  9. self._view = view
  10. self._view.setRange(0,10)
  11. self._view.connect(self._view,
  12. QtCore.SIGNAL("valueChanged(int)"),
  13. self.changeRpm)
  14. def changeRpm(self, rpm):
  15. if self._model:
  16. self._model.setRpm(rpm*1000)
  17. def notify(self):
  18. self._view.setValue(self._model.rpm()/1000)

The setup now can simply make use of off-the-shelf QDial and QSlider instances

  1. dial = QtGui.QDial(container)
  2. dial_controller = DialController()
  3. dial_controller.setView(dial)
  4. dial_controller.setModel(engine)
  5. slider = QtGui.QSlider(container)
  6. slider_controller = SliderController()
  7. slider_controller.setView(slider)
  8. slider_controller.setModel(engine)

The Model-GUI-Mediator approach basically has the Controller adapt the
off-the-shelf widget to be aware of the Model. This requires no subclassing. In
a sense, Model-GUI-Mediator is similar to Document-View, but it reorganizes
competences in a different way and splits the View into off-the-shelf
functionality and application-contextual functionality.