Using MVC when the model is dynamic

Discussion in 'Python' started by pysim, Sep 30, 2003.

  1. pysim

    pysim Guest

    Hi, I have a couple of general requests for pointers to python
    examples and design advice.

    I'm looking for examples of MVC-based GUI controls done in python
    (model-view-controller).

    Also, examples where something like a simulation model running in its
    own thread sends fast updates to a GUI in near real-time. The only
    examples I've seen, such as SimPy, wait until the model is finished
    running before outputting data.

    Most MVC controls I see in java for example are only designed for
    infrequent user-driven changes, like resizing a window or mouse
    clicks, not cases where the underlying model is changing itself over
    time.
     
    pysim, Sep 30, 2003
    #1
    1. Advertising

  2. pysim wrote:
    > Hi, I have a couple of general requests for pointers to python
    > examples and design advice.
    >
    > I'm looking for examples of MVC-based GUI controls done in python
    > (model-view-controller).
    >
    > Also, examples where something like a simulation model running in its
    > own thread sends fast updates to a GUI in near real-time. The only
    > examples I've seen, such as SimPy, wait until the model is finished
    > running before outputting data.
    >
    > Most MVC controls I see in java for example are only designed for
    > infrequent user-driven changes, like resizing a window or mouse
    > clicks, not cases where the underlying model is changing itself over
    > time.


    Here's a baroque example. Maybe it will inspire others to respond
    with something more concise :)

    The attached example app has a text-based "GUI", as I wanted it to
    be more or less self-contained. The app prints a sinusoidal
    line of "#"s, until you type "q" and hit return. E.g.

    #
    #
    #
    #
    #
    #
    #
    #
    #
    #
    #
    #
    ....

    The classes in the example include:

    Observable -- maintains a set of observers, notifying them whenever
    the value of the Observable changes.

    Model -- runs a simple computation in a background thread. The
    Model's state is an Observable.

    View -- displays a model's state as shown above. Also processes
    keyboard input, looking for a line starting with 'Q' or 'q' as
    a shutdown request.

    App -- acts like a controller, but also includes the application's
    main event loop, which blocks until keyboard input is available.


    The Model and View know nothing about each other. The
    App glues them together; it tells the View to update whenever
    the Model state changes. It also routes user input (keyboard
    input, in this case) to the View. This is a common way
    to build MVC applications, and it addresses your concern about
    controls which are designed only for user-driven changes.


    There's a lot of synchronization overhead in updating the view.
    If you have lots of views on the same model, or if your views
    take a long time to pain, performance and responsiveness will
    suffer. In such cases it might be good to have the model publish
    its state only at regular intervals -- store its current state
    in a protected attribute and only occasionally copy that state
    into an Observable.

    Or, if you want to write really baroque code (like me,
    apparently :) you could use a "delayed" Observable which
    propagates model state changes at low rates. For example, the
    model might publish its state using an instance of this
    (untested) class:

    class DelayedObservable(Observable):
    ...
    def __init__(self, notifyInterval=1.0):
    self._tLastChange = time.time()
    self._notifyInterval = notifyInterval # Seconds

    def _notify(self, force=0):
    dt = time.time() - self._tLastChange
    if force or (dt >= self._notifyInterval):
    for observer in self._observers:
    ...
    self._tLastChange = time.time()


    This is all pretty verbose, but maybe it will be helpful.

    For better ideas on limiting the rate of propagation of model
    state changes, it might be good to look at the documentation
    for NSNotificationQueue. It's part of the Cocoa framework of
    Mac OS X.

    --
    Mitch

    #!/usr/bin/env python
    """Demo a basic MVC app structure in which
    The model is a simulation running in its own thread.
    The model undergoes frequent, View-able state changes
    """

    import sys, sets, threading, select, math


    class Observable(object):
    """An Observable notifies its observers whenever its value changes.
    Observers are just Python callables having the signature
    callMe(sender)

    Lots of MT-safe overhead, here. So...
    TO DO: Demonstrate asynchronous, coalesced notifications."""
    def __init__(self):
    self._observers = sets.Set()
    self._lock = threading.RLock()
    self._value = None

    def addObserver(self, newObserver):
    self._observers.add(newObserver) # This oughtta be locked...

    def removeObserver(self, anObserver):
    self._observers.remove(anObserver) # This oughtta be locked...

    def _notify(self):
    for observer in self._observers: # This oughtta be locked...
    try:
    observer(self)
    except:
    pass # Don't let one broken observer gum up everything

    def _getValue(self):
    self._lock.acquire()
    result = self._value
    self._lock.release()
    return result

    def _setValue(self, newValue):
    self._lock.acquire()
    self._value = newValue
    self._lock.release()
    self._notify()

    value = property(_getValue, _setValue, None, "The observable value")


    class Model(threading.Thread):
    """Computes new values asynchronously. Notifies observers whenever
    its state changes."""
    def __init__(self, **kw):
    threading.Thread.__init__(self, **kw)
    self._stopped = 0
    self._state = Observable()

    def onStateChange(self, observer):
    self._state.addObserver(observer)

    def removeStateChange(self, observer):
    self._state.removeObserver(observer)

    def run(self):
    """Run the model in its own thread."""
    self._stopped = 0
    i = 0.0
    di = math.pi / 8.0
    while not self._stopped:
    self._state.value = math.sin(i)
    i += di

    def stop(self):
    self._stopped = 1


    class View:
    """Dummy 'view' just prints the model's current value whenever
    that value changes, and responds to keyboard input."""
    def __init__(self):
    self._onQuitCB = None

    def modelStateChanged(self, modelState):
    valueBar = " " * int((1 + modelState.value) * 10)
    print "%s#" % valueBar

    def onQuit(self, newOnQuitCB):
    self._onQuitCB = newOnQuitCB

    def handleInput(self, userInput):
    if userInput.lower().startswith("q"):
    if self._onQuitCB:
    self._onQuitCB(self)


    class App:
    """This sample application computes and displays garbage, at
    a high rate of speed, until the user quits."""
    def __init__(self):
    # Yep, this is really a controller and not just an app runner.
    self._model = Model()
    self._view = View()
    self._terminated = 0

    self._model.onStateChange(self._view.modelStateChanged)
    self._view.onQuit(self._quitApp)

    def run(self):
    self._model.start()

    self._terminated = 0
    while not self._terminated:
    ins, outs, errs = select.select([sys.stdin], [], [])
    if ins:
    self._view.handleInput(raw_input())

    self._model.join()

    def _quitApp(self, *args):
    self._terminated = 1
    self._model.stop()
    self._model.removeStateChange(self._view.modelStateChanged)


    def main():
    """Module mainline (for standalone execution)"""
    theApp = App()
    theApp.run()

    if __name__ == "__main__":
    main()
     
    Mitch Chapman, Oct 1, 2003
    #2
    1. Advertising

  3. pysim

    Brian Kelley Guest

    Mitch Chapman wrote:

    > Model -- runs a simple computation in a background thread. The
    > Model's state is an Observable.
    >
    > View -- displays a model's state as shown above. Also processes
    > keyboard input, looking for a line starting with 'Q' or 'q' as
    > a shutdown request.
    >
    > App -- acts like a controller, but also includes the application's
    > main event loop, which blocks until keyboard input is available.
    >
    >
    > The Model and View know nothing about each other. The
    > App glues them together; it tells the View to update whenever
    > the Model state changes. It also routes user input (keyboard
    > input, in this case) to the View. This is a common way
    > to build MVC applications, and it addresses your concern about
    > controls which are designed only for user-driven changes.


    Hi Mitch :) Note that Mitch's example won't work on windows since you
    can't use select on stdin (only sockets).

    I have been playing with two approaches to this issue of dynamically
    changing events from a model.

    1) I've recently become a fan of using the GUI's native events to
    control state from the model to the view. They are thread-safe and easy
    to implement. wxPython has a good example in their demo of doing this.
    See the Threads entry of "Process and Events"

    Model -> state is an observable ->| generates an event |<- View responds

    when the observable is altered a GUI event is posted through the
    callback scheme. The gui can then asynchronously respond to changes in
    states. Warning: sometimes multiple instances of the same event
    handler can be run simultaneously so be careful here. I usually prevent
    this through some locking mechanism.

    This is pretty much the same as Mitch's example except the observable is
    decoupled from the View through the event mechanism. I expect the same
    could be accomplished using a Queue and a timer.

    2) Spawn an independent process and simply monitor the processes
    standard output and respond accordingly. See the LongRunningTasks entry
    on http://staffa.wi.mit.edu/people/kelley

    Both of these gloss over the issue of how does the view talk to the
    model. The only real answer I have is polling. The model must
    occasionally poll for new instructions from the view or have it's own
    event-style manager.

    Brian Kelley
     
    Brian Kelley, Oct 1, 2003
    #3
  4. In article <3f7af8bf$0$564$>,
    Brian Kelley <> wrote:
    .
    .
    .
    >1) I've recently become a fan of using the GUI's native events to
    >control state from the model to the view. They are thread-safe and easy
    >to implement. wxPython has a good example in their demo of doing this.
    > See the Threads entry of "Process and Events"
    >
    >Model -> state is an observable ->| generates an event |<- View responds

    .
    .
    .
    >Both of these gloss over the issue of how does the view talk to the
    >model. The only real answer I have is polling. The model must
    >occasionally poll for new instructions from the view or have it's own
    >event-style manager.
    >
    >Brian Kelley
    >


    My summary: yes, what the original poster requested ab-
    solutely is feasible, and even convenient, with Python.

    If, that is, I understand him or her correctly. I admit
    to confusion, though. He or she wrote:
    # Also, examples where something like a simulation model running in its
    # own thread sends fast updates to a GUI in near real-time. The only
    # examples I've seen, such as SimPy, wait until the model is finished
    # running before outputting data.
    #
    # Most MVC controls I see in java for example are only designed for
    # infrequent user-driven changes, like resizing a window or mouse
    # clicks, not cases where the underlying model is changing itself over
    # time.
    Was an interest for fast communications from user to model
    desired? Or was the question solely along the lines of, is
    there an architecture that'll permit the View to keep up
    with the Model more-or-less in real time?
    --

    Cameron Laird <>
    Business: http://www.Phaseit.net
    Personal: http://phaseit.net/claird/home.html
     
    Cameron Laird, Oct 1, 2003
    #4
  5. pysim

    Brian Kelley Guest

    Cameron Laird wrote:
    >>Both of these gloss over the issue of how does the view talk to the
    >>model. The only real answer I have is polling. The model must
    >>occasionally poll for new instructions from the view or have it's own
    >>event-style manager.
    >>

    > Was an interest for fast communications from user to model
    > desired? Or was the question solely along the lines of, is
    > there an architecture that'll permit the View to keep up
    > with the Model more-or-less in real time?


    You are correct in what the original poster wanted (i.e. is
    there an architecture that'll permit the View to keep up
    with the Model more-or-less in real time). I was just being pedantic :)

    Brian
     
    Brian Kelley, Oct 2, 2003
    #5
  6. Brian Kelley wrote:
    > Cameron Laird wrote:
    >
    >>> Both of these gloss over the issue of how does the view talk to the
    >>> model. The only real answer I have is polling. The model must
    >>> occasionally poll for new instructions from the view or have it's own
    >>> event-style manager.
    >>>

    >> Was an interest for fast communications from user to model
    >> desired? Or was the question solely along the lines of, is
    >> there an architecture that'll permit the View to keep up
    >> with the Model more-or-less in real time?

    >
    >
    > You are correct in what the original poster wanted (i.e. is
    > there an architecture that'll permit the View to keep up
    > with the Model more-or-less in real time). I was just being pedantic :)


    Hi Brian :)

    I'm confused by the comments about the model needing to poll for new
    instructions from the view. (BTW does this mean you usually prefer to
    combine view and controller responsibilities in a single entity, rather
    than implement them separately?)

    Why not just have the model provide control methods which clients can
    invoke directly (t. ex. the stop() method in the example I posted)?
    Are you saying this doesn't fit well in wxPython, that it's more
    natural in that environment to communicate via event codes and queues?

    If that's the case, can you define a model-controller class which
    receives view events and translates them into method invocations on
    an associated model? That does seem like a lot of work, but it
    would let the model remain ignorant of -- loosely coupled to --
    its observers.

    --
    Mitch
     
    Mitch Chapman, Oct 2, 2003
    #6
  7. In article <YkPeb.10891$>,
    Mitch Chapman <> wrote:
    .
    .
    .
    >I'm confused by the comments about the model needing to poll for new
    >instructions from the view. (BTW does this mean you usually prefer to
    >combine view and controller responsibilities in a single entity, rather
    >than implement them separately?)

    This *is* an apt question. Is there something
    about wxWindows that pushes one in this direc-
    tion, or was it just an abbreviation of the
    evident fact that both the View and Controller
    connect to the same end-user?
    >
    >Why not just have the model provide control methods which clients can
    >invoke directly (t. ex. the stop() method in the example I posted)?
    >Are you saying this doesn't fit well in wxPython, that it's more
    >natural in that environment to communicate via event codes and queues?
    >
    >If that's the case, can you define a model-controller class which
    >receives view events and translates them into method invocations on
    >an associated model? That does seem like a lot of work, but it
    >would let the model remain ignorant of -- loosely coupled to --
    >its observers.

    My experience in this area is that this sort
    of reliance on synthetic events is *not* "a
    lot of work". I've found it quite rewarding.
    You're right: it healthily decouples M from
    V from C.
    .
    .
    .
    --

    Cameron Laird <>
    Business: http://www.Phaseit.net
    Personal: http://phaseit.net/claird/home.html
     
    Cameron Laird, Oct 2, 2003
    #7
  8. pysim

    Brian Kelley Guest

    Cameron Laird wrote:

    > In article <YkPeb.10891$>,
    > Mitch Chapman <> wrote:
    >
    >>I'm confused by the comments about the model needing to poll for new
    >>instructions from the view. (BTW does this mean you usually prefer to
    >>combine view and controller responsibilities in a single entity, rather
    >>than implement them separately?)

    >


    Maybe when I say polling it is a symantic issue, but in your run method:

    def run(self):
    """Run the model in its own thread."""
    self._stopped = 0
    i = 0.0
    di = math.pi / 8.0
    while not self._stopped:
    self._state.value = math.sin(i)
    i += di

    isn't "while not self._stopped" a method of polling? This variable is
    still set asynchronously. This is the only point that I was getting at,
    for example if you had many states in your model that can be altered,
    you still need to periodically check to affect these state changes.

    >>If that's the case, can you define a model-controller class which
    >>receives view events and translates them into method invocations on
    >>an associated model? That does seem like a lot of work, but it
    >>would let the model remain ignorant of -- loosely coupled to --
    >>its observers.


    Yes. This is actually what I do. I still consider this in the realm of
    polling though (note that I am talking about receiving the model change
    event and converting it to a method invocation here). Using the normal
    threading model the polling is hidden as in your example, i.e. keep
    checking to see if self._stopped is true.

    Perhaps polling is not the proper way to talk about this. But, tt helps
    my mental picture of what is going on though in that "at any time there
    could be a state change so if you are interested in it, keep checking
    for the change." This mental picture works (for me at least) when
    running the model in a thread or running the model in another process.

    Brian.
     
    Brian Kelley, Oct 2, 2003
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Guest

    MVC Model in .Net

    Guest, Nov 10, 2003, in forum: ASP .Net
    Replies:
    7
    Views:
    2,184
    Jeff Louie
    Nov 29, 2003
  2. Edward A Thompson
    Replies:
    2
    Views:
    629
    Tim Jowers
    Jul 14, 2003
  3. rmn190
    Replies:
    2
    Views:
    2,368
    Arne Vajhøj
    Jan 10, 2008
  4. Paulo
    Replies:
    3
    Views:
    482
    Peter Bromberg [C# MVP]
    Dec 4, 2008
  5. Crazy Cat
    Replies:
    1
    Views:
    691
    schepp
    Sep 3, 2009
Loading...

Share This Page