Graphical MVP confusion

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Graphical MVP confusion

Janos Kazsoki
Hi,

the deeper I try to understand the working of the MVP model in
graphical cases, the more I sink into confusion.

Can you help me on this methodological issue?

Ian sent a beautiful example in an earlier e-mail with the followings:

"" NB1 This is very "bare bones" but should give you some ideas.
NB2 This is not the only way and some of these classes are not really
needed in this example.
Create an Object subclass called MyDrawingTool
Add one method -
drawOn: aCanvas within: aRectangle
   aCanvas ellipse: aRectangle
Create a Model subclass called MyModel
Add one method -
drawingTool
   ^MyDrawingTool new
Create a Presenter subclass called MyPresenter
Add one class method -
defaultModel
   ^MyModel new
Create a View subclass called MyView
Add two methods -
initialize
   super initialize.
   self setModel: MyModel new
onPaintRequired: aPaintEvent
   | canvas |
   canvas := aPaintEvent canvas.
   self model drawingTool
       drawOn: canvas
       within: ((Point zero extent: self extent) insetBy: 100)
Finally, open a workspace and evaluate the following. This creates a
resource of MyView (it will now appear in the resources list) and makes
it the default view for MyPresenter. The #initialize above method just
ensures that we open the resource in the ViewComposer even when it does
not have an attached Presenter/Model.
MyPresenter addView: MyView asResource: 'Default view'
You can now go the presenter and edit it's default view, as normal, to
set the background colour etc.
NB. You have now created an MVP triad that appears in the resource list
and which can be dropped onto any composite presenter in just the same
way as any existing resources. You can now add code to MyModel to
change the drawingTool answered (probably by adding more DrawingTool
classes to the image) and also code to MyPresenter to allow input (a
pop up menu for example) to change triads drawing tool on the fly.""

This works beautiful until.... until I try to "add code to MyModel to
change the drawingTool answered"

Let's say I would like to differentiate that in one case I would like
to draw a line, in another an ellipse, and so on, i.e. a graphical
object can be a line, a stroke, an ellipse and so on. And the job of
the draw is different in each case. Where do I put the differentiating
logic: in the Model? In the Presenter? In the View?

I studied intensively the graphical examples, and the graphical goodies
of the masters (OA, Ian, Bill, Chris), but my confusion only inreased.

The pieces of the mosaic in my understanding are as follows:

In Scribble the logic is in the Presenter (the DrawOn. in the Scribble
presenter). I can imagine this as follows: I create an
OrderedCollection, where the elements can be #line, #dot, #ellipse...
and in the presenter I call the different drawing routines.

Also Chris's excellent Graphic Base Goodie seems to delegate the Draw
function into the presenter, and it is very elegant in the sense that
the view does not even know about it's model, so there is not even a
model in it..

Other places (like Bill) tell sometimes, that it is good, if the model
knows, how to draw itself. This would mean the logic of drawing line,
ellipse... should be in the model...

And obviously all of them work.

So my confusion is that at the end the view is responsible to "repaint"
itself, or a part of itself (a small rectangle). In other cases you put
the logic into the "graphical" presenter part. In other cases "the
model knows how to draw itself"...

I tend to think, that I must put a logic into the presenter, which goes
through the OrderedCollection one by one, and for each element it
executes the "LineDrawOn:", the "DotDrawOn:", the EllipseDrawOn:"
depending on the type of the element.

Only I have have a bad feeling about this approach, because it looks
for me too procedural: in the presenter a big classical "if then else"
chain, although the Line, the Dot, the Ellipse are (or can be)
different objects in the model, which "know themselves, how to draw
themselves".

Can you bring me light into my darkness?
Many thanks in advance,
Janos


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Ian Bartholomew-19
Janos,

> the deeper I try to understand the working of the MVP model in
> graphical cases, the more I sink into confusion.
[]
> Can you bring me light into my darkness?

It's a bit of a wimp answer but I don't think there is any right or wrong
way, it depends on what you are trying to achieve.

For example....

It can sometimes make most sense to put drawing code in the View.  Something
like drawing a watermark, permanent icon or grid lines on every page.

Sometimes the Model might feel right, when the Model contains the data that
is being drawn as a graph for example - although putting the code in the
View might be just as sensible.

Sometimes it is better to create individual objects that draw themselves (as
in Scribble, Playground or BouncingBalls).

Personally I like lots of classes :-) so to display different shapes, as
described in your post, I would probably do something like the Playground
demo.

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Schwab,Wilhelm K
In reply to this post by Janos Kazsoki
Janos,

In general (as you have noted), I recommend separating the drawing from
the medium.  Doing so allows you to test the code without running into
the problems that result from having troubles on presenter startup.

Note that the above is not inconsistent with any other approach.  You
can still invoke the separate rendering component from anywhere you
want.  By keeping the graphics "non-graphical", you can use a do-it to
generate a bitmap.  Early on, you would expect walkbacks, so don't even
bother with a presenter.  Fix the problems until it runs.  Then, use an
image presenter to look at the bitmap.  Still not right?  Fix that.
Then you can apply the graphics anywhere, especially if you utilize the
resolution of the canvas.

Re updating the display, there is no magic solution.  One approach is to
always draw to bitmaps and to use ImagePresenter to display them.  The
trick is to invalidate the correct portion of the view's client area
after changes (more below).

On the other extreme, you can let individual objects draw on a canvas
that turns out to be the view's canvas - note that the separate drawing
object(s) concept works here too.  Efficiency results from limiting the
number of objects that need to be drawn.  This usually starts as either
an object that has changed, or by some portion of the view being
uncovered, either of which can be reduced to an invalid rectangle.  Note
that a long/thin object affect a large rectangle (life is seldom fair<g>).

Given the invalid rectangle, do the best you can at discarding objects
that you know do not interesect the rectangle, and do not draw them.  Do
draw the others.  Again consider a long/thin object.  It might go from
far to the left to far to the right of the rectangle, with only a small
portion of it interesecting the rectangle.  It must be drawn, but you
might do well to inform it of the rectangle so it can draw only what is
needed.  With large numbers of small objects, a boolean test on
intersection goes a long way.

Clearly if you choose to cache graphics in a bitmap, then it needs to be
updated as above, and then the view needs to be updated too, though the
latter is simply #invalidate: followed by a bitblt (hopefully the view
is smart enough to look at its invalid rect in the paint struct and draw
only that much of the bitmap.

All of this was illustrated very well in the early MFC tutorials.  Yes,
Bill is giving Microsoft credit for doing something right - mark your
calendars :)   However, the most recent time I looked at the MFC
tutorial (which has been a while), it went on an on about "smooth
scrolling" and did not appear to address optimization that I could find.

Also, have you noticed the little pieces of "left over selection" that
sometimes appear in Microsoft lists and trees?  The screen scarring that
occurs with the animated menu effects?  That stuff is a symptom of
incorrect drawing.  You need to tell the model what is going on so that
it can correctly update the display _any_ time that it must be drawn,
not only on selection, etc.  Those things that scar the screen do so
because somebody thought it was sufficient to invert a rectangle on
selection, rather than altering the model and then invalidating the
relevant rectangle, or otherwise at least arranging for it to do so
should it be asked to update the display.  Put another way, if you draw
in two places, they need to agree, or you need to be certain they never
interact (which is a risky assumption when the machine is busy and the
user wants to do work rather than watch cool graphics).

Does that help?

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Chris Uppal-3
In reply to this post by Janos Kazsoki
Janos Kazsoki wrote:

> So my confusion is that at the end the view is responsible to "repaint"
> itself, or a part of itself (a small rectangle). In other cases you put
> the logic into the "graphical" presenter part. In other cases "the
> model knows how to draw itself"...

It's an interesting issue.  There is, as Ian said, no single correct answer.
The responsibility for rendering may end up on the Model, View, Presenter, or
somewhere else.  All can be "best" in some specific situation, but what
determines when ?

BTW, a lot of this stuff I hadn't worked out before you raised the question --
I had been just following my instincts, and had never really tried to work out
/why/ I've done things the way(s) I have.  (Which means that this post will be
rather long -- I'm thinking aloud...)

Anyway, here's my (current) take on it:

I think the answer is not very much about "good" software structure (giving
responsibility to the right objects) so much as it is about more general issues
of software lifecycle and re-use.

Let's start with a rough characterisation of the standard components in MVP in
those terms:

Models are domain-specific rather than application-specific or generic.  If
there are several applications in the same domain then they are likely to share
Model code.  On the other hand, an application (even a similar application) in
a different domain would use different models.  (There are also some "dumb"
generic model classes that go with generic views, but I don't think they affect
the picture much.)

The Presenters are application-specific, rather than domain-specific or
generic.  Of course there are some "dumb" generic Presenter classes (such as
ListPresenter) that go with generic Views, but any interesting application will
have at least one "interesting" Presenter -- and once you've described what
those Presenters do, you have also described your application.

The Views are generic, pluggable, objects.  They are assembled by aggregation
to make application-specific GUIs.  It is rare to write a View at all, let
alone write an application-specific one.  In general a View is conceptually
very simple (even though it may have a complicated implementation).  You can
usually describe what it does in one short sentence.  Because they are intended
to be generic, there's a strong tendency for Views to have pluggable behaviour
and lots of settable "attributes".  Of course, an important point about Views
is that they are supposed to work well with the View Composer.  Typically you
write a View class once and then re-use it in many un-related projects.  But,
once a View is written, you don't often touch it again (apart from bug-fixes,
and maybe a few more-or-less trivial enhancements).  That's mostly because each
View is conceptually simple, so there's not much to change, but also because it
is rather awkward to modify View code once existing instances have been saved
as resources (just as a practical point) .

So, when we have graphics to draw, who gets the job ?

In some cases it may be clear that it's the Model's job.  If the Model is
inherently a graphical "thing" then it seems silly for it not to know how to do
graphics.  (But I'll return to this point later.)  For instance, a Diagram or
Map (in the geographical sense) might be inherently graphical.  Many
applications in the domain that use a Diagram or Map will have a graphical
element, and the Models know how to provide that.

In rather more cases, the Model is not inherently graphical. Often the graphics
will be application-dependent, and in such cases it seems that some Presenter
should take responsibility for it.  It can't be handed off to the Model because
that shouldn't know about graphics ("not my job, mate!"), and it isn't generic
enough for a View to handle naturally; but it fits nicely into the MVP
framework for one of the "interesting" (application-specific) Presenters to
take on the job. OA's "Scribble" is example of this kind of thinking (IIRC).
And in my 'Graphics Base' package, the PaintingPresenter is designed
specifically for this case -- you can either subclass it or handle its
#paintCanvasRequired:in: event in a higher-level Presenter.  The PaintableView
objects just provide simple generic graphical services like double-buffering.
(BTW, that package was written some time ago, and I now think it needs a number
of improvements.)

Lastly, the graphics may be generic.  In that case the natural implementation
is for a View to handle it.  E.g. given some "standard" file format for
graphics, PDF perhaps, the job of rendering PDF on-screen is a very generic
job, and a specific View class for rendering that format would be useful.  (The
file, or something derived from it would be the Model).

It's natural, as you write several applications, to discover common code and
move that into some kind of more generic framework.  In the case of graphics,
that means there's a tendency for graphics code to start off in Presenters, but
after a while get generalised and move into some View.

(Incidentally, one of the things that I've realised is wrong about my 'Graphics
Base' package, is that I've ended up using PaintableView as a base-class for a
number of such "generic" graphical Views.  It's handy as a base-class because
the new Views can inherit services it provides, like double-buffering, but
really that's not a good hierarchy.  I should have an abstract base class that
provides the services, and the PaintableView and the other Views can all
inherit from that.)

Returning to the case where the Model does the graphics.  There are a few
reasons for wanting to factor out the responsibility for the graphics into a
fourth component.  One is that graphics code tends to be complicated/intricate,
and it can just over-complicate the Model.  Another is that there will often be
/some/ part of the graphical code that is purely about presentation (choice of
colour-scheme for instance).  So you can end up with anything from a "Settings"
object that holds presentation-specific state (colours, line-widths, and so
on), through to a full-blown "Renderer" object which knows how to draw a Model
on a Canvas.  In either case the Model holds domain-specific state, whereas the
Settings/Renderer is much more generic (it may even be fully generic if it's a
mere "Settings" object), and whatever state it holds is more relevant to
presentation than to the domain.

In a similar way, if the View has responsibility for graphics it may still be
worthwhile splitting the code out into an associated helper object.  One reason
is, again, that Views are already pretty complicated, and adding complicated
graphics code too can muddle things up.  Another reason is that it makes it
easier to paint the same graphics in other contexts (printing for instance, or
in testing/development as Bill mentioned).  Such an object would be a
Settings/Renderer object in much the same way as above (but, since it's
attached to the View, is almost certain to be fully generic).

In either of the above two cases, it can be a good idea to make the helper
object an editable aspect of the View.  That allows you to edit it in the View
Composer, which can make life quite a bit easier.

(Yet /another/ thing that's missing in my graphics framework is a generic
"RenderingView" which holds a Renderer, and uses that to paint its Model on the
screen.)

And that's it.  No simple answers, I'm afraid.  At least I understand what I've
been doing now ;-)

But I'll have to rewrite 'CU Graphics Base'...

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Chris Uppal-3
In reply to this post by Schwab,Wilhelm K
Bill,

[just a couple of thoughts]

[re: separating the drawing from the medium]
> Doing so allows you to test the code without running into
> the problems that result from having troubles on presenter startup.

Nah, can't be bothered with all that.  Just splat it in the window and debug it
if it breaks...

(Of course, you do need an error catcher around the code before you can risk
that ;-)


[re: optimised repainting of only the "wrong" bit]
> All of this was illustrated very well in the early MFC tutorials.  Yes,
> Bill is giving Microsoft credit for doing something right - mark your
> calendars :)   However, the most recent time I looked at the MFC
> tutorial (which has been a while), it went on an on about "smooth
> scrolling" and did not appear to address optimization that I could find.

Possibly because they feel that for simple graphics on a modern machine,
there's little or no need for optimisation.  Or at least that it's an
"advanced" concept, not needed by everyone.  If so, then I think they are right
(this /is/ a day for the calendars, folks!).


> Also, have you noticed the little pieces of "left over selection" that
> sometimes appear in Microsoft lists and trees?  The screen scarring that
> occurs with the animated menu effects?  That stuff is a symptom of
> incorrect drawing.

Which in turn is probably caused by someone trying to optimise the painting,
and getting it /almost/ right...

;-)

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Chris Uppal-3
In reply to this post by Janos Kazsoki
Janos Kazsoki wrote:

> I tend to think, that I must put a logic into the presenter, which goes
> through the OrderedCollection one by one, and for each element it
> executes the "LineDrawOn:", the "DotDrawOn:", the EllipseDrawOn:"
> depending on the type of the element.
>
> Only I have have a bad feeling about this approach, because it looks
> for me too procedural: [...]

Hmm... missed that bit.

So here's part II :-)

If your models, or parts of your models, are really such "graphical" objects as
names like Ellipse, Line, Dot, suggest, then I'd say it would be natural for
them to know how to draw themselves.

That's not the most common situation, though.  If your objects are things like
ConnectingRod, Bolt, GearChain, then it would be rather odd if they knew how to
draw themselves.  And it would be downright weird if objects like
EndOfYearStatement, SeasonallyAdjustedEconomicIndicators, PhysicalInventory,
knew.  In such cases, I think it's much more likely that your painting code
wouldn't need the kind of test-and-switch logic, since it would be asking the
model for data and using the answers to construct the drawing ("how many gears
are there and where are they placed ?"  Ok, I'll draw circles here, here, and
here.  "What was the largest monthly gain ?", right I'll draw a grid this
high... and so on).

I doubt if there are many applications that don't fall into one or the other of
the above pattern.  One kind of application that doesn't fit is when you have
to render externally-defined data.  For instance if you were given a "graphics"
file that consisted of lines like:

    E 0 0 100 100 0xFF000
    C 50 50 50 0x0
    ...
(meaning
    ellipse from (0,0) to (100,00), red
    circle at (50,50) radius 50, black
    ...)

then you'd have to have a cascade of tests, or a dictionary lookup, somewhere
in your rendering code (whether that code is in your Model, View, Presenter or
anywhere else).  Even if you converted each line into a "proper" graphical
object (Ellipse, Circle,...) which knew how to paint itself, you'd still have
needed tests or a dictionary to convert the lines into objects in the first
place.  In such a case it would probably not be worth creating all the objects
unless they had something else important to do besides just painting
themselves.

In intermediate cases it might be worth considering double-dispatch.  The
rendering object runs down a collection of model objects

        aCollection do: [:each | each renderWith: self on: aCanvas].

and the model objects each implement #renderWith:on by double-dispatching back
to the renderer.  A GearChain's implementation would do:

    aRenderer renderGearChain: self on: aCanvas.

whereas a Bolt's implementation of the same method would do:

    aRenderer renderBolt: self on: aCanvas.

and so on...

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Janos Kazsoki
Thank you all for the help.

Now I see light at the end of the tunnel.
I study them at the weekend, and try out.

Thanks again,
Janos


Reply | Threaded
Open this post in threaded view
|

Re: Graphical MVP confusion

Schwab,Wilhelm K
In reply to this post by Chris Uppal-3
Chris,

> [just a couple of thoughts]
>
> [re: separating the drawing from the medium]
>
>>Doing so allows you to test the code without running into
>>the problems that result from having troubles on presenter startup.
>
>
> Nah, can't be bothered with all that.  Just splat it in the window and debug it
> if it breaks...

Ok, but that leaves you with a view (arguably the stickiest creatures
with respect to STB versioning), and starting from scratch when it's
time to render on a printer.  Split it, and you fix both problems, and
the view is trival to write in terms of the renderer.


>>All of this was illustrated very well in the early MFC tutorials.  Yes,
>>Bill is giving Microsoft credit for doing something right - mark your
>>calendars :)   However, the most recent time I looked at the MFC
>>tutorial (which has been a while), it went on an on about "smooth
>>scrolling" and did not appear to address optimization that I could find.
>
> Possibly because they feel that for simple graphics on a modern machine,
> there's little or no need for optimisation.  

I suspect they do, and I submit that this is part of their trouble.



>>Also, have you noticed the little pieces of "left over selection" that
>>sometimes appear in Microsoft lists and trees?  The screen scarring that
>>occurs with the animated menu effects?  That stuff is a symptom of
>>incorrect drawing.
>
>
> Which in turn is probably caused by someone trying to optimise the painting,
> and getting it /almost/ right...

But it's nowhere close to right, at least not conceptually.  I fear that
they see the selection as being tied to the mouse event, and don't
realize the need to handle other cases.

Writing Solid Code is dated now, but it describes a process involving
experienced programmers roughing in design (reasonable) and turning it
over to less experienced programmers to fill in functionality (also
reasonable).  However, it will work only if there is adequate
supervision and mentoring, and that would be the first thing to get cut.

Go back even farther (win3.1 days) as described in Barbarians Lead by
Bill Gates (or whatever it's called), and you will read about
programmers having two machines, one of which was slow - by mandate.
Why pay somebody lots of money and then hobble them with a slow machine?
  Because that's what the end users had.  If they still thought like
that, they'd do more testing under load (if only by accident), and they
would see the redraw problems.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]