…
Yeah, the problem is mostly over-thinking the level of
abstraction.
It will just give you a
headache. Programmers are taught to think in abstractions,
but there’s a point where abstractions lose any practical
benefit and actually cause you to come up with overly
complex designs to solve simple problems. Even MVC vs. MVP
seems to me to be based on a misunderstanding of what MVC
is. MVP, from what I’ve read of it, reads like someone
learning MVC by reading articles, misunderstanding it, and
creating MVP to ‘fix’ what they saw was wrong with MVC,
which IMHO is just re-creating MVC and calling it by another
name.
I will use the term MVC, but if you want to
consider what I’m saying MVP that’s fine.
Here's an easy to understand way of writing more
MVC-compliant apps with wxPython. Take your wx.Frame
subclass with everything in it, convert it to an
wx.EvtHandler subclass that takes a wx.Frame as an argument
(along with any data objects you need). Then replace any
calls to wx.Frame API from self.Whatever to
self.frame.Whatever. Then, when your app starts, initialize
your frame, your data objects, and then initialize your
EvtHandler subclass and pass them in, like so:
class MyController(wx.EvtHandler):
def
init(self, frame, data):
self.frame
= frame
self.data
= data
#
create child controls
self.button
= wx.Button(self.frame, -1, “Load”)
…
#
bind events
self.button.Bind(wx.EVT_BUTTON,
self.OnLoadClicked)
def
OnLoadClicked(self, event):
self.LoadData()
def
LoadData(self):
self.data.load_data()
#
populate wx.Frame controls with data
class MyApp(wx.App):
def
OnInit(self):
frame
= wx.Frame(None, -1, “MVC Frame”)
data
= MyDataStructure()
self.controller
= MyController(frame, data)
#
eventually…
#
self.controller1 = DataViewController(frame, data)
#
self.controller2 = AnimationController(frame)
#
etc.
frame.Show()
return
True
Now all your code is no longer in a wx.Frame subclass. :)
And you’ll note, a small app written this way will have only
a few more lines of code than a traditional, all in wx.Frame
app does. People way overestimate what is required or
expected of MVC abstraction. They think that if you use MVC
you must break everything into tiny, tiny bits. You don’t
need to. A small MVC app will look much like a traditional
wx app does, as you can see above. However, as the app
grows, this model makes refactoring and reuse simpler. When
you add new code, instead of having only one choice, that
is, to just stuff it in the wx.Frame subclass, you can
choose - if you feel the code’s reusable, you can make a
new, shareable controller for it. If not, you can just put
it your primary controller, just the same as you’d do if
your wx.Frame was the event handler. You may always do it
that way. But the key is that you have that choice.
It's really that simple. Move your code from a wx.Frame
subclass into a wx.EvtHandler subclass and you’re doing all
that’s required. You may never make more than one Controller
for your app. It’s okay. You aren’t angering the MVC gods by
doing so.
The point of adopting MVC is that it
facilitates creating discrete controllers for discrete
behaviors when that is the best approach, though small apps
may never need to.
Anyway, I do appreciate that until the toolkit itself
considers adopting some of these ideas for its code, it’s
hard to get a solid grasp of the concept, and it feels like
you’re fighting the toolkit to use it. Even as I promote
MVC, I myself find it hard to give up on the traditional way
of writing wxPython code since I’m working with a lot of
legacy code or code written by others. When you see and work
with that code all the time, the path of least resistance is
to simply stick with that model for everything. However,
since I code with other toolkits, I also know the pitfalls
of the wx approach, and I don’t think it’s good to rest on
one’s laurels and stop looking for better ways of doing
things. I should probably at least clean up my
InlineTextSearchController and contribute it, so that people
who need inline text search can use it and get an idea of
how controllers work.
This is a bit long, hope others don't mind and hopefully this might
be useful for other MVC noobs.
I recently started to rewrite my app to apply what I have learned
over the last few years (mainly on this list , by reading the
“books” and by lots of trial and error) and hopefully come up with
something better, easier to maintain and to extend.
While I followed the different MVC/MVP discussions over the years on
this list I never got a feeling that I grasp it enough and have
started the re-write without applying either of these patterns, but
I have second thoughts after seeing Kevin’s post above which at
first sight makes MVC simpler/easier to understand.
But - applying it to my own stuff is another story.
I use a database and I use SQLAlchemy's declarative, so all the SQL
to Python mapping is done by SA and I put all that into a package
“model” which I like to keep compatible to what e.g. Turbogears
would expect in it.
The app will be a Wine book and a Recipe book, so these are my two
main controllers? Then there are a bunch of dialogs to maintain
master data etc such as country, region, wine type etc etc, so each
of those would be a controller?
For my re-write I decided to start doing part of the sa model, and
widget stuff and test them with the master data maintenance dialogs.
In an app like this where is the main code of e.g.
“CreateItemClicked” method going?
I was trying to explain it in words where I think things should go,
but some pseudo code is probably better.
controllers.base - contains all the common code to setup controls
etc which is currently in my dialog_base model which I would need to
change to be a wx.EvtHandler based class.
controllers.country - inherits from base
def __init__(self, view, model):
self.CreateControls(view, dictWithInfoForControls)
self.view = view
self.model = model
def CreateItemClicked(self, evt):
self.model.CreateItem(self._isLocalized)
self.LoadData()
self.EnableControls()
def LoadData(self):
self.InitDialog() # I use validator.TransferToWindow to load
indiv. controls data
def SaveData(self):
self.TransferDataFromWindow()
self.model.SaveData()
self.InitDialog()
def EnableControls()
self.DoEnableControls(self.stdCtrls, True)
if self._isLocalized:
self.DoEnableControls(self.locCtrls, True)
self.locCtrls[0].SetFocus()
else:
self.stdCtrls[0].SetFocus()
# buttons
self.save.Enable()
self.saveAndClose.Enable()
self.undo.Enable()
self.create.Enable()
model.country
def CreateItem(isloc):
self.dbItem = modelsa.country
modelsa.session.add(self.dbItem)
if isloc:
self.dbItemLoc = modelsa.country_localized
self.dbItemLoc.language = wx.GetApp().userlanguage
modelsa.session.add(self.dbItemLoc)
view.country
- a sized_controls.SizedDialog
def __init__(self, title='MVC maintain country information):
self.data = model.country()
self.controller
= controller.country(self, self.data)
In other words, how do I check/make sure that things are in the
right place (model, view, controller)?
Werner
···
On 09/25/2011 06:54 PM, Kevin Ollivier wrote: