A Question of Style

My multi-module application has methods in each file that defines a
notebook page for the UI. I'd like to create a separate file for all these
methods, and include that module in all the others.

   Is this acceptable practice in python/wxPython coding? My goal is to be
more efficient by aggregating function code in a couple of different modules
(e.g., wxPython methods, mathematical methods, plotting methods).

TIA,

Rich

···

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Rich Shepard wrote:

  My multi-module application has methods in each file that defines a
notebook page for the UI. I'd like to create a separate file for all these
methods, and include that module in all the others.

  Is this acceptable practice in python/wxPython coding? My goal is to be
more efficient by aggregating function code in a couple of different modules
(e.g., wxPython methods, mathematical methods, plotting methods).

Sure. Some big apps will have hundreds of modules or more. It's your code, so organize it how it makes sense to you. The only thing you need to watch out for is circular imports (A imports B which imports C which imports A.)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Thank you, Robin.

   I'll be careful to make imports semi-circular: A, B, and C each import X,
Y, and Z.

   On to rearranging code ....

Rich

···

On Fri, 20 Oct 2006, Robin Dunn wrote:

Sure. Some big apps will have hundreds of modules or more. It's your
code, so organize it how it makes sense to you. The only thing you need
to watch out for is circular imports (A imports B which imports C which
imports A.)

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Robin Dunn wrote:

Rich Shepard wrote:

    ... snip ...

  Is this acceptable practice in python/wxPython coding? My goal is to be
more efficient by aggregating function code in a couple of different modules
(e.g., wxPython methods, mathematical methods, plotting methods).

Sure. Some big apps will have hundreds of modules or more. It's your code, so organize it how it makes sense to you. The only thing you need to watch out for is circular imports (A imports B which imports C which imports A.)

I did something along this line in an application that draws together several complex interactive steps, with each step "living" in a notebook page. There's a file for the application itself, a file for each page, and for some pages, files for their model classes, separating UI concerns from database access concerns.

I used XRCed to create the GUI. For each notebook page panel, I specified a subclass of the form modulename.classname, e.g. "AutoCleaningPanel.AutoCleaningPanel". Then, in the main file, I added an import statement for each, e.g., "from AutoCleaningPanel import AutoCleaningPanel" (in this case, the module and class names are identical). This was enough to allow the XRC loader to find the classes.

···

--
Don Dwiggins
Advanced Publishing Technology

I did something along this line in an application that draws together
several complex interactive steps, with each step "living" in a notebook
page. There's a file for the application itself, a file for each page,
and for some pages, files for their model classes, separating UI concerns
from database access concerns.

Don,

   This is similar to how I want to restructure my application.

I used XRCed to create the GUI. For each notebook page panel, I specified a subclass of the form modulename.classname, e.g. "AutoCleaningPanel.AutoCleaningPanel". Then, in the main file, I added an import statement for each, e.g., "from AutoCleaningPanel import AutoCleaningPanel" (in this case, the module and class names are identical). This was enough to allow the XRC loader to find the classes.

   I've not looked at all at XRC. I have discovered that if I put all the
methods in a file by themselves, and import them in the main project file as
'from wxMethods import *', they're not seen by python. Perhaps the methods
need to be in a class of their own; I'll look into the reason.

Thanks,

Rich

···

On Fri, 20 Oct 2006, Don Dwiggins wrote:

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Rich Shepard wrote:

  I've not looked at all at XRC. I have discovered that if I put all the
methods in a file by themselves, and import them in the main project file as
'from wxMethods import *', they're not seen by python. Perhaps the methods
need to be in a class of their own; I'll look into the reason.

I'm not sure what you mean by "methods" here. Your import will get all the top-level declared objects in the module, including variables, functions, and classes. If by "methods" you mean functions declared in the context of a class (the usual interpretation), what the import will give you is the class; you'll access a method by instantiating the class and calling it as "instance.method(args)".

···

--
Don Dwiggins
Advanced Publishing Technology

Well, now my newness to python and wxPython floats to the top. I've taken
all the methods for the GUI and put them in a new file, wxMethods.py, and in
the class guiMethods.

   In the main file, eikos.py, I have:

from wxMethods import guiMethods

but there's an error at the top of the menudata method:

   File "eikos.py", line 78, in menuData
     return (('&File',
AttributeError: 'MainFrame' object has no attribute 'OnFileNew'

   The method, 'OnFileNew' is called thusly:

   def menuData(self):
     return (('&File',
               ('&New \tCtrl-N', 'Create a new project', self.OnFileNew),
     ...

   I'm obviously referencing the method incorrectly, but I don't see my
error. A pointer would be very helpful.

TIA,

Rich

···

On Fri, 20 Oct 2006, Robin Dunn wrote:

Sure. Some big apps will have hundreds of modules or more. It's your
code, so organize it how it makes sense to you. The only thing you need
to watch out for is circular imports (A imports B which imports C which
imports A.)

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

I'm not sure without seeing all the code, but maybe you are trying to implement it the way it could be done in C++, where the methods of a class can be spread across several source files. Can't be done in Python; you'll need to have your class methods call code in other source files.

The error message is saying "This class does not have a member function called 'OnFileNew'". One useful debugging idea is
   print dir(self)
which lists all the object's attributes. Note that they are held as a dictionary; grokking the way dictionaries are used to hold a class instance's member variables and functions is a big step forward in understanding Python.

HTH, Phil

···

At 04:15 PM 10/20/2006, you wrote:

On Fri, 20 Oct 2006, Robin Dunn wrote:

Sure. Some big apps will have hundreds of modules or more. It's your
code, so organize it how it makes sense to you. The only thing you need
to watch out for is circular imports (A imports B which imports C which
imports A.)

  Well, now my newness to python and wxPython floats to the top. I've taken
all the methods for the GUI and put them in a new file, wxMethods.py, and in
the class guiMethods.

  In the main file, eikos.py, I have:

from wxMethods import guiMethods

but there's an error at the top of the menudata method:

  File "eikos.py", line 78, in menuData
    return (('&File',
AttributeError: 'MainFrame' object has no attribute 'OnFileNew'

  The method, 'OnFileNew' is called thusly:

  def menuData(self):
    return (('&File',
              ('&New \tCtrl-N', 'Create a new project', self.OnFileNew),
                ...

  I'm obviously referencing the method incorrectly, but I don't see my
error. A pointer would be very helpful.

  The method, 'OnFileNew' is called thusly:

  def menuData(self):
    return (('&File',
              ('&New \tCtrl-N', 'Create a new project', self.OnFileNew),
                ...
  I'm obviously referencing the method incorrectly, but I don't see my
error. A pointer would be very helpful.

I'm not sure without seeing all the code, but maybe you are trying to implement it the way it could be done in C++, where the methods of a class can be spread across several source files. Can't be done in Python;

That's not entirely true: you just can't call them quite the same way. For example, from the above:

def menuData(self):
      return (('&File',
               ('&New \tCtrl-N', 'Create a new project', self.OnFileNew),

"self.InfileNew" means that InFileNew is a member of this class (self). If you had written:

def menuData(self):
      return (('&File',
                ('&New \tCtrl-N',
                 'Create a new project',
                 MethodsModule.OnFileNew),

it way have worked.

However, the main point is true -- you generally don't want to do that, methods belong in the class that they are used in -- that's why they are called methods, rather than functions. In the above, I suspect that OnfileNew belongs in the Frame subclass you're writing -- that's where menu handlers usually go.

It's still not totally clear why you are trying to re-factor in this way, but I suspect it's to follow the DRY (Don't Repeat Yourself) principle. This is a good instinct. If you find yourself writing the same code in more than one place -- it's time for a refactor.

What I'm guessing is that you have a few classes that all share similar methods, so you tried to put those in their own file. That can be done but it's not the OO way to do it. The OO way is subclassing. If you have a bunch of notebook pages, they should probably all be derived from the same class -- the methods common to all of them go there, and that superclass can be defined in it's own module, if you like.

If things get more complicated, it could be time for multiple inheritance and mix-ins. If you google mix-ins, you'll probably get lots of stuff, but here's quick explanation:

There are times when you have a bunch of classes which share some of the same functionality, but not all. I finally got this when I tried to apply DRY to wx.lib.floatcanvas, so I'll use that as an example:

All the objects you might want to draw on the screen are derived from DrawObject. This class holds common functionality for all kinds of shapes. However, I found that there are multiple types of objects, all with overlapping functionality: For instance, some have only a line (lines, polylines), some have both a line and a fill ( circles, polygons, rectangles). Some have their position defined in terms of a single (X,Y) point (circles, text), some by a set of points (Polygons, polylines). To accommodate this, I set up a bunch of mixins - classes that don't do anything by themselves, but provide methods that are comon to more than one, but not all of the DrawObjects:

PointsObjectMixin
LineOnlyMixin
LineAndFillMixin

etc, etc.

Now to make a new kind of DrawObject, you can jsut piece together the functionality required, and most of the work is done with the mixins. For example, the Line class has just two of its own methods:

class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
     def __init__(self,Points,
                  LineColor = "Black",
                  LineStyle = "Solid",
                  LineWidth = 1,
                  InForeground = False):
         DrawObject.__init__(self, InForeground)

         ...

     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
  
         ...

- I hope that helps,

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris,

   I thought this was the case when I asked my original question. It's now
obvious to me that I did not ask it clearly enough for everyone to
understand it and respond that methods must be included in the class in
which they are declared.

   So, I'm back on track and appreciate the insight you all have given to me.

Rich

···

On Mon, 23 Oct 2006, Christopher Barker wrote:

However, the main point is true -- you generally don't want to do that,
methods belong in the class that they are used in -- that's why they are
called methods, rather than functions. In the above, I suspect that
OnfileNew belongs in the Frame subclass you're writing -- that's where
menu handlers usually go.

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

While we're on the topic of separating UI from data model concerns, I followed the example of Model-View-Presenter outlined here with pretty good results. There is a bit of work involved in adding new functionality to your form (add the GUI controls to the view, logic to the presenter, and bind events to methods of the presenter in the interactor) but it's worth it for the cleaner code you end up with.
http://wiki.wxpython.org/index.cgi/ModelViewPresenter

Robert

···

On 2006/10/21, at 7:32, Rich Shepard wrote:

On Fri, 20 Oct 2006, Don Dwiggins wrote:

I did something along this line in an application that draws together
several complex interactive steps, with each step "living" in a notebook
page. There's a file for the application itself, a file for each page,
and for some pages, files for their model classes, separating UI concerns
from database access concerns.

Don,

  This is similar to how I want to restructure my application.

Robert,

   Thanks for the reminder. I'll go look at this again.

Rich

···

On Tue, 24 Oct 2006, Robert Gravina wrote:

While we're on the topic of separating UI from data model concerns, I
followed the example of Model-View-Presenter outlined here with pretty
good results. There is a bit of work involved in adding new functionality
to your form (add the GUI controls to the view, logic to the presenter,
and bind events to methods of the presenter in the interactor) but it's
worth it for the cleaner code you end up with.
http://wiki.wxpython.org/index.cgi/ModelViewPresenter

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc.(TM) | Accelerator
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863