4. Scalar presenters


4.1 Introduction

4.1.1 Terminology

Presenter
View
Cell

In Rationale 3.1, a presenter is defined as an object that shows or displays a view to the application user. The presenter is also responsible for connecting the view to its corresponding model. A scalar presenter is one that presents a "scalar" view, which essentially corresponds to a GUI widget connected to a scalar model, a cell. Later we will look at composite presenters, which present composite views connected to composite models (structures). This distinction is blurred, however, by presenters such as the ListBox, which present a single widget connected to a composite model. Nevertheless, the ListBox will be described in this section, since it displays a single widget.

4.1.2 Incremental definition

4.1.2.1 Technique

The general technique of evolving a framework was introduced previously. Here we more specifically evolve four presenters, and except for the Button presenter, each is developed in two phases: (1) making connections from the model/cell to the view, and (2) making connections to the model. The Button presenter, of course, has no associated model. As always, the definitions are displayed in boxes with a white background, while the unit tests are given a green background. For the most part, these are presented in the order in which they were originally investigated.

4.1.2.2 Initial import statements

Since the investigation is carried out as a suite of unit tests, the module begins by importing the unittest module. The views needed are imported from the ui module, and the models are imported from the abstraction module as ab. This means that all view classes have the prefix ui. and all model classes the ab. prefix.

import unittest
from donnal.thought import ui
from donnal.thought import abstraction as ab

4.2 TextField presenter

4.2.1 Connection from model

class TextField_000(ui.TextWidget):
    """Bare bones presenter; nothing but the view"""
    def __init__(self, parent=None):
        ui.TextWidget.__init__(self, parent)


class Test000(unittest.TestCase):
    """Evolving the TextField presenter: connection from model"""

    def test001(self):
        """pre TextField: bare view"""
        x = TextField_000()
        x.SetValue(4321)
        self.assertEqual(x.GetValue(), '4321')

...

class TextField_001(ui.TextWidget):
    """Minimal presenter, start evolution from here."""
    modelType = ab.StringCell

    def __init__(self, parent=None):
        ui.TextWidget.__init__(self, parent)
        self._model = self.modelType()
        self._model.AddObserver(self)
        self.Update(self._model)

    def Update(self, source):
        """The presenter is an observer of the model."""
        self.SetValue(self._model.value)

    def GetModel(self):
        return self._model

    # class Test000 (continued)
    def test002(self):
        """pre TextField: observes a default model"""
        x = TextField_001()
        m = x.GetModel()
        m.value = 3344
        self.assertEqual(x.GetValue(), '3344')

...

class TextField_002(TextField_001):
    """Pass a model instance as parameter to presenter"""
    def __init__(self, parent=None, model=None):
        ui.TextWidget.__init__(self, parent)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        self.Update(self._model)

    # class Test000 (continued)
    def test003(self):
        """pre TextField: observes a default model"""
        x = TextField_002()
        m = x.GetModel()
        m.value = 3344
        self.assertEqual(x.GetValue(), '3344')

    def test004(self):
        """pre TextField: observes a given model instance"""
        m = ab.StringCell()
        m.value = 'at init'
        x = TextField_002(model=m)
        self.assertEqual(x.GetValue(), 'at init')
        m.value = 3344
        self.assertEqual(x.GetValue(), '3344')

Connection to model

class TextField_010(TextField_001):
    """Connect event trigger to event-handler method"""
    def __init__(self, parent=None, model=None):
        ui.TextWidget.__init__(self, parent)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        self.Update(self._model)
        ui.EVT_KILL_FOCUS(self, self.OnLoseFocus)

    def OnLoseFocus(self, event):
        """Public event handler, may be overridden if necessary"""
        self._model.Set(self.GetValue())

class Test010(unittest.TestCase):
    """Evolving the TextField presenter: connection to model"""

    def test001(self):
        """TextField: bi-directional connection"""
        m = ab.StringCell()
        x = TextField_010(None, m)  # model m, presenter x

        # from model to view
        m.value = 'first'           # this is the model value
        self.assertEqual(x.GetValue(), 'first')   # view follows

        # from view to model
        x.SetValue('second')        # this is the view value
        x.trigger1('evt')           # textwidget has lost focus
        self.assertEqual(m.value, 'second')       # model follows

class TextField_015(TextField_010):
    """Pass outside parameters to event-handler method"""
    def __init__(self, parent=None, model=None, *args, **kwds):
        ui.TextWidget.__init__(self, parent)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        self._args = args
        self._kwds = kwds
        self.Update(self._model)
        ui.EVT_KILL_FOCUS(self, self._handle_KILL_FOCUS)

    def _handle_KILL_FOCUS(self, event):
        """Private event handler to call public event handler"""
        self.OnLoseFocus(event, self._model,
                         *self._args, **self._kwds)

    def OnLoseFocus(self, event, model):
        """Public event handler, may be overridden if necessary"""
        model.Set(self.GetValue())

    # class Test010 (continued)
    def test002(self):
        """TextField: passing no parameters works like before"""
        m = ab.StringCell()
        x = TextField_015(None, m)  # model m, presenter x

        # from model to view
        m.value = 'first'           # this is the model value
        self.assertEqual(x.GetValue(), 'first')   # view follows

        # from view to model
        x.SetValue('second')        # this is the view value
        x.trigger1('evt')           # textwidget has lost focus
        self.assertEqual(m.value, 'second')       # model follows

....

class TextField_016(TextField_015):
    """Use outside parameter(s) passed to event-handler method"""
    def OnLoseFocus(self, event, model, other):
        other.Set(self.GetValue())

    # class Test010 (continued)
    def test003(self):
        """TextField: positional parameter used"""
        m = ab.StringCell()
        o = ab.StringCell()
        x = TextField_016(None, m, o)  # model m, other o

        # from model to view
        m.value = 'first'           # this is the model value
        self.assertEqual(x.GetValue(), 'first')   # view follows

        # from view to 'other'
        x.trigger1('evt')           # textwidget has lost focus
        self.assertEqual(o.value, 'first')       # other follows

    def test004(self):
        """TextField: keyword parameter used"""
        o = ab.StringCell()
        x = TextField_016(other=o)  # model m, other o
        m = x.GetModel()

        # from model to view
        m.value = 'first'           # this is the model value
        self.assertEqual(x.GetValue(), 'first')   # view follows

        # from view to 'other'
        x.trigger1('evt')           # textwidget has lost focus
        self.assertEqual(o.value, 'first')       # other follows

4.3 Button presenter

class Button_001(ui.Button):
    """A simple presenter/controller (no model) using ui.Button"""
    def __init__(self, parent=None, *args, **kwds):
        ui.Button.__init__(self, parent)
        self._args = args
        self._kwds = kwds
        ui.EVT_BUTTON(self, self._handle_BUTTON)

    def _handle_BUTTON(self, event):
        """Private event handler to call public event handler"""
        self.OnClick(event, *self._args, **self._kwds)

    def OnClick(self, event, *args, **kwd):
        """Abstract event handler. Override in application"""
        pass

class MyButton(Button_001):
    """Use outside parameter(s) passed to event-handler method"""
    def OnClick(self, event, other):
        other.Set('hello')

class TestButton(unittest.TestCase):
    """Test all aspects of the Button presenter. Not 'evolved'."""

    def test_000_001(self):
        """Button: positional parameter used"""
        o = ab.StringCell()
        x = MyButton(None, o)  # other o

        # from view to 'other'
        x.trigger1('evt')                       # button clicked
        self.assertEqual(o.value, 'hello')      # other follows

    def test_000_002(self):
        """Button: keyword parameter used"""
        o = ab.StringCell()
        x = MyButton(other=o)  # other o

        # from view to 'other'
        x.trigger1('evt')                       # button clicked
        self.assertEqual(o.value, 'hello')      # other follows

4.4 Choice presenter

Connection from model

class Choice_001(ui.Choice):
    """Minimal presenter, start evolution from here."""
    modelType = ab.StringCell

    def __init__(self, parent=None):
        ui.Choice.__init__(self, parent, choices=None)
        self._model = self.modelType()
        self._model.AddObserver(self)
        for i in self._model.GetChoices():
            self.Append(i)
        self.Update(self._model)

    def Update(self, source):
        """The presenter is an observer of the model."""
        sel = self.FindString(self._model.value)
        if sel == -1:
            sel = 0
        self.SetSelection(sel)

    def GetModel(self):
        return self._model


class Choice_002(Choice_001):
    """Pass a model instance as parameter to presenter"""
    def __init__(self, parent=None, model=None):
        ui.Choice.__init__(self, parent, choices=None)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        for i in self._model.GetChoices():
            self.Append(i)
        self.Update(self._model)

class MyStringCell(ab.StringCell):
    choices = ['papa bear', 'mama bear', 'baby bear']

class Test000(unittest.TestCase):
    """Evolving the Choice presenter: connection from model"""

    def test001(self):
        """pre Choice: FindString"""
        y = Choice_001()
        y.Append('one')
        y.Append('two')
        self.assertEqual(y.FindString('one'), 0)
        self.assertEqual(y.FindString('two'), 1)
        self.assertEqual(y.FindString('three'), -1)

    def test002(self):
        """pre Choice: pass model to presenter"""
        x = MyStringCell()
        y = Choice_002(model=x)
        self.assertEqual(y.FindString('papa bear'), 0)
        self.assertEqual(y.FindString('mama bear'), 1)
        self.assertEqual(y.FindString('baby bear'), 2)
        self.assertEqual(y.FindString('no bear'), -1)
        self.assertEqual(y.GetSelection(), 0)
        x.value = 'baby bear'
        self.assertEqual(y.GetSelection(), 2)

Connection to model

class Choice_010(Choice_002):
    """Connect event trigger to event-handler method"""
    def __init__(self, parent=None, model=None):
        ui.Choice.__init__(self, parent, choices=None)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        for i in self._model.GetChoices():
            self.Append(i)
        self.Update(self._model)
        ui.EVT_CHOICE(self, self.OnSelect)

    def OnSelect(self, event):
        """Public event handler, may be overridden if necessary"""
        self._model.Set(self.GetString(self.GetSelection()))


class Choice_015(Choice_010):
    """Pass outside parameters to event-handler method"""
    def __init__(self, parent=None, model=None, *args, **kwds):
        ui.Choice.__init__(self, parent, choices=None)
        self._model = model or self.modelType()
        self._model.AddObserver(self)
        for i in self._model.GetChoices():
            self.Append(i)
        self._args = args
        self._kwds = kwds
        self.Update(self._model)
        ui.EVT_CHOICE(self, self._handle_CHOICE)

    def _handle_CHOICE(self, event):
        """Private event handler to call public event handler"""
        self.OnSelect(event, self._model,
                         *self._args, **self._kwds)

    def OnSelect(self, event, model):
        """Public event handler, may be overridden if necessary"""
        model.Set(self.GetString(self.GetSelection()))

class OtherChoice(Choice_015):
    """Use outside parameter(s) passed to event-handler method"""
    def OnSelect(self, event, model, other):
        other.Set(self.GetSelection())
        model.Set(self.GetString(other.value))

class Test010(unittest.TestCase):
    """Evolving the Choice presenter: connection to model"""

    def test001(self):
        """pre Choice: bi-directional connection"""
        m = MyStringCell()
        x = Choice_010(None, m)  # model m, presenter x

        # from model to view
        m.value = 'baby bear'       # this is the model value
        self.assertEqual(x.GetSelection(), 2)   # view follows

        # from view to model
        self.assertEqual(m.value, 'baby bear')  # starting value
        x.trigger1(1)               # select second item
        self.assertEqual(m.value, 'mama bear')  # model follows

    def test002(self):
        """pre Choice: passing extra paramenters, none given"""
        m = MyStringCell()
        x = Choice_015(None, m)  # model m, presenter x

        # from model to view
        m.value = 'baby bear'       # this is the model value
        self.assertEqual(x.GetSelection(), 2)   # view follows

        # from view to model
        self.assertEqual(m.value, 'baby bear')  # starting value
        x.trigger1(1)               # select second item
        self.assertEqual(m.value, 'mama bear')  # model follows

    def test003(self):
        """pre Choice: positional parameter used"""
        m = MyStringCell()
        o = ab.StringCell()
        x = OtherChoice(None, m, o)     # model m, other o

        # from model to view
        m.value = 'baby bear'       # this is the model value
        self.assertEqual(x.GetSelection(), 2)   # view follows

        # from view to model
        self.assertEqual(m.value, 'baby bear')  # starting value
        x.trigger1(1)               # select second item
        self.assertEqual(m.value, 'mama bear')  # model follows
        self.assertEqual(o.value, '1')          # other follows

    def test004(self):
        """pre Choice: keyword parameter used"""
        m = MyStringCell()
        o = ab.StringCell()
        x = OtherChoice(other=o, model=m)

        # from model to view
        m.value = 'baby bear'       # this is the model value
        self.assertEqual(x.GetSelection(), 2)   # view follows

        # from view to model
        self.assertEqual(m.value, 'baby bear')  # starting value
        x.trigger1(1)               # select second item
        self.assertEqual(m.value, 'mama bear')  # model follows
        self.assertEqual(o.value, '1')          # other follows

4.5 ListBox presenter

Connection from model

class TestBoxStructure(ab.Structure):
    
    def Assemble(self):
        self.Add('something', ab.StringCell)
        
    def Get(self):
        return self.something.value


class TestBoxTable(ab.Table):
    item = TestBoxStructure


class ListBox_001(ui.ListBox):
    """Minimal presenter, start evolution from here."""
    model = TestBoxTable
    sortkey = 'something'

    def __init__(self, parent=None):
        ui.ListBox.__init__(self, parent, choices=None)
        self._model = self.model()
        self._model.AddObserver(self)
        self.Update(self._model)

    def Update(self, source):
        """The presenter is an observer of the model."""
        self.Clear()
        for i in self._model.GetSortedList(self.sortkey):
            self.Append(i[0], i[2])     # text, data; i[1] is name

    def GetModel(self):
        return self._model


class ListBox_002(ListBox_001):
    """Pass a model instance as parameter to presenter"""
    def __init__(self, parent=None, model=None):
        ui.ListBox.__init__(self, parent, choices=None)
        if model == None:
            self._model = self.model()
        else:
            self._model = model
        self._model.AddObserver(self)
        self.Update(self._model)

class Test000(unittest.TestCase):
    """Evolving the ListBox presenter: connection from model"""

    def test001(self):
        """pre ListBox: default model, component values"""
        x = ListBox_001()
        m = x.GetModel()
        y = m.Add()
        y.something.value = 'bb'
        y.something.NotifyAncestors()
        self.assertEqual(x.GetString(0), 'bb')
        y = m.Add()
        self.assertEqual(x.GetString(0), 'bb')
        self.assertEqual(x.GetString(1), None)
        y.something.value = 'aa'
        y.something.NotifyAncestors()
        self.assertEqual(x.GetString(0), 'aa')
        self.assertEqual(x.GetString(1), 'bb')

    def test002(self):
        """pre ListBox: passing a given model instance"""
        m = TestBoxTable()
        y = m.Add()
        y.something.value = 'bb'
        y = m.Add()
        y.something.value = 'aa'
        x = ListBox_002(model=m)
        self.failUnless(m is x.GetModel())
        self.assertEqual(x.GetString(0), 'aa')
        self.assertEqual(x.GetString(1), 'bb')

Connection to application

class ListBox_010(ListBox_002):
    """Pass a model instance as parameter to presenter"""
    def __init__(self, parent=None, model=None, *args, **kwds):
        ui.ListBox.__init__(self, parent, choices=None)
        if model == None:
            self._model = self.model()
        else:
            self._model = model
        self._model.AddObserver(self)
        self._args = args
        self._kwds = kwds
        self.Update(self._model)
        ui.EVT_LISTBOX(self, self._handle_EVT_LISTBOX)
        ui.EVT_LISTBOX_DCLICK(self, self._handle_EVT_DCLICK)

    def _handle_EVT_LISTBOX(self, event):
        """Private event handler to call public event handler"""
        self.OnSelect(event, *self._args, **self._kwds)

    def _handle_EVT_DCLICK(self, event):
        """Private event handler to call public event handler"""
        self.OnDClick(event, *self._args, **self._kwds)

    def OnSelect(self, event, *args, **kwds):
        """Abstract event handler. Override in application."""
        pass

    def OnDClick(self, event, *args, **kwds):
        """Abstract event handler. Override in application."""
        pass


class MyListBox(ListBox_010):

    def OnSelect(self, event, first, second):
        """Public event handler."""
        cd = self.GetClientData(event)
        if cd != None:
            val = cd.something.value
        else:
            val = ''
        first.something.Set(val)

    def OnDClick(self, event, first, second):
        """Public event handler."""
        cd = self.GetClientData(event)
        if cd != None:
            val = cd.something.value
        else:
            val = ''
        second.something.Set(val)


class Test010(unittest.TestCase):
    """Evolving the ListBox presenter: connection to app"""

    def test001(self):
        """pre ListBox: controller aspect of presenter"""
        a = TestBoxStructure()
        b = TestBoxStructure()
        m = TestBoxTable()
        y = m.Add()
        y.something.value = 'bb'
        x = MyListBox(None, m, a, b)

        # from model to view
        y = m.Add()
        y.something.value = 'aa'
        y.something.NotifyAncestors()
        self.assertEqual(x.GetString(0), 'aa')
        self.assertEqual(x.GetString(1), 'bb')

        # from view to 'first'
        x.trigger1(0)
        self.assertEqual(a.something.value, 'aa')
        self.assertEqual(b.something.value, '')
        x.trigger2(1)
        self.assertEqual(a.something.value, 'bb')
        self.assertEqual(b.something.value, 'bb')

4.6 Discussion

...


XHTML 1.0 © 2003 Donnal C. Walter,
This page updated 2003-12-11.
See About this document for information on suggesting changes.