straw-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Straw-devel] the preferences dialog, model-view-presenter and debug_aro


From: Juri Pakaste
Subject: [Straw-devel] the preferences dialog, model-view-presenter and debug_around
Date: Sun, 06 Jun 2004 00:31:08 +0300

This is one of those horribly long emails I'm in habit of sending out to
the list. This should interest you if you care about the way the
preferences dialog looks and feels or are interested in the separation
of logic and ui thing I was talking about earlier. Or just if you are
going to implement some new displays in Straw.

First, the surface:

The categories tab in the preferences dialog looks a bit different now.
I'll explain here why I did it the way I did, because I like it this way
and I know it isn't the way it used to work and was going to work.

The previous feed list wasn't very useful. I'll call it list A in the
following discussion. It was going to be get a companion that would have
contained all the feeds. Let's call that B. You would have dragged feeds
from B to A to make them members of the category. 

However, in this case, I think the approach of selecting which feeds
belong using a simple list with toggles is more logical. The other
approach would have felt a bit weird, because you wouldn't remove the
feeds from B when dragging, but you couldn't drag multiple copies of the
feed to A. And to remove them from A... drag them to B? That would have
made no sense. There would also have been buttons for these operations,
which wouldn't have the second problem, but would have had the first
one.

The other significant difference is in sorting. I believe that using the
normal list sorting operations is not the right approach here. That's
why there are those two new buttons. Which, incidentally, look a lot
like the buttons we used to have back in the early days.

With the main window and the preferences dialog, we have two views to
the same data. Spatial isn't the right word to use here, but it's
familiar in the Gnome jargon. So: we are not spatial. There's no direct
connection between the views and the data. I know I can't claim to know
how a user of Straw thinks about these things. But at least to me,
clicking on the header row in a treeview signifies sorting of the
*view*. Not the data. I think that using separate buttons for it makes
it easier to understand that you're not here manipulating the view in
question - after all, they are separate from the list view - but the
underlying data.

Oh, and I dropped the Properties button. I think it was mostly just
confusing there.

Second, under the hood:

I've mentioned that Straw's code structure needs some work. There isn't
enough separation of logic and ui. The traditional approach to this is
Model-View-Controller. Model-View-Presenter is a newer pattern, designed
to fix some of the shortcomings of MVC. I've never used it before, but I
decided to give it a try. This is a decent intro:
http://www.object-arts.com/EducationCentre/Overviews/ModelViewPresenter.htm

So now the stuff in the categories tab uses MVP, as I understand it. The
result is a large number of classes and definitely more lines of code
than before. There's also more functionality, but the fact is that most
of the code is there because of MVP.

My approach to MVP, adapted to Straw, is the following:

Model: this is the domain level object. Things like FeedList are used as
models. It knows nothing of any user interface concerns.

View: this is the object encapsulating the Gtk+ code. It receives events
from the model and acts on those. It also holds a reference to the model
so it can query it for information when needed. The view also does Gtk+
event handling but it doesn't try to do anything fancy with the events.
It usually just calls the presenter - it also has a direct reference to
it and uses normal function calls here - and makes it deal with whatever
happened. However, it passes no reference to any Gtk+ object to the
presenter. 

Presenter: it's kind of the boss here. The model is the most important
object, but presenter decides what happens in the UI. I'm not sure if
it's kosher, but I'm not using a model (with the buttons) or even a view
in all the places (well, calling CategoriesPresenter a presenter is kind
of stretching it, but so be it.) Presenter receives direct function
calls from the View. It holds references to the view object and the
model. It knows about the user interface, but it doesn't use Gtk+. This
is important for testing. You can replace the View object with a dummy
to test the presenter and model. The presenter emits signals to
communicate outside the MVP triangle.

I haven't MVPized the fields and buttons below the feed list in the
categories tab, the rest uses it. I put two base classes to MVP.py and
use those in the code. It might be possible to splice some more classes
into the inheritance hierarchy; for example, maybe there could be a
ListView that subclasses WidgetView.

For now, the pattern has worked pretty well. I have had to add some new,
more finegrained events to the Category objects, but beyond that, it's
been pretty simple (easy to say now that I've got it working :-).

I'll try to go MVP on the rest of the stuff in the categories tab, but
not right now. At some point, we should also try writing some tests. It
might require splitting the classes into separate modules, though - the
presenters might have to live in modules that don't import gtk to be
tested. If things seem to work out ok, we could gradually convert old
code to use this. There are drawbacks: the increased complexity does
hurt a bit. If we can make improvements to the MVP.py base classes it
might make things a bit easier, but we can't avoid it completely. It
still is a large number of classes, even if the class definitions are
short.

Oh, and: if you don't follow straw-commits actively, there's a rather
useful new function in error.py, debug_around.

This code:

def foo(x):
        print x
foo = error.debug_around(foo)
foo(42)

results in output like this:
file:line:class: Entering foo
file:line:class: Args: (42)
42
file:line:class: Exiting foo

Where file, line and class refer to the line 
foo = error.debug_around(foo)

Depends on your development habits, but I usually insert lots of
log("enter") calls etc - this makes it easier and allows you to leave
the function body alone. Here's hoping we get the decorator syntax in
Python 2.4.

-- 
[ Juri Pakaste | address@hidden | http://www.iki.fi/juri/ ]

Attachment: signature.asc
Description: This is a digitally signed message part


reply via email to

[Prev in Thread] Current Thread [Next in Thread]