Struggling to understand OOP, need advice on best way to proceed

Hi all.

I’ve beed developing an app for Structural Geology called OpenStereo. It works, but since I started writing it as I was learning python/wxpython, and my only programming base prior to that was some Pascal/C, I end up with a software that lacks the

elegance and advantages od OOP. I really don’t get how to use classes in my app.

Let me explain a little more:

The program must open files with orientation data and it does a lot of stuff with it. Plots in graphs called stereonets, makes

rose diagrams, histograms etc. Now, I don’t know how to use classes, but from what I could get, once I define something

(a variable) in one class, I can ‘broadcast’ that value to all parts of my program, right? So far I use a lot of pubsub and I end up creating the same data several times (one for the stereonet, another for histograms…).

So I thought this: when a file is opened (and I must be able to open several files or even the same file more than once) I store its values in a variable called ‘datasets’, which is a dictionary and each entry in it is a dictionary with all the data from the file, as well as more stuff (graphic properties etc). But this dictionary must grow/shrink when I open/delete a file, and I must be able to change some of its values any time.

So, how to do it?

Should I create a class to hold this variable and then import it in the others? (each class - histogram etc - is in a different file, and since they correspond to elements of the GUI, each is a wxPanel or wxFrame)

thanks,

Carlos

···

Prof. Carlos Henrique Grohmann
Institute of Geosciences - Univ. of São Paulo, Brazil

  • Digital Terrain Analysis | GIS | Remote Sensing -

http://carlosgrohmann.com


Can’t stop the signal.

Sent with Sparrow

Hi,

A bit tricky to understand what you problem really is, but it sounds
like you may have trouble iwth Python's name spaces and scoping rule,s
rather than OO questions.

read this:

http://python.net/crew/mwh/hacks/objectthink.html

but in the meantime...

A few comments:

Now, I don't know how to use classes, but
from what I could get, once I define something
(a variable) in one class, I can 'broadcast' that
value to all parts of my program, right?

no, not at all.

python is based on objects and name spaces, every object exists on
it's own (more or less), and is bound to a name in various name spaces
(or stored in a container) so:

a = 'this'

There is now a string object, containing "this", the name 'a' is bound
to it in the current scope.

b = a

now the name 'b' is also bound to that same object.

del a

now a is no longer bound to that object, but b still is.

l = [b, "some", "more"]

now there is a reference to that same object in teh list, l.

del b

the object is still in that list!

classes are really just another name space:

class test:
    pass

# a class that does nothing

T = test()

and instance of that class

T.a = l

now there is a name, "a" in the instance namespace that points to the
list I created before.

if I change l:

l.append("some more stuff")

l will have changed, and T.a has also changed -- it is the same object.

so:

I store its values in a variable
called 'datasets', which is a dictionary and each
entry in it is a
dictionary with all the data from the file, as well
as more stuff (graphic
properties etc). But this dictionary must grow/shrink > when I open/delete a
file, and I must be able to change some of its values any time.

So, how to do it?

no problem -- dictionaries are "mutable", just like the list above --
you can change its contents, and all the names pointing to it will
still refer to the same object.

Should I create a class to hold this variable and
then import it in the
others?

classes are a way to group together the data you have, and the
functions that manipulate that data -- if all you have is data, then
using a plain dict is fine -- if you have a bunch of functions that
manipulate that data, then putting them in a class together makes
sense. It changes nothing at all about how to reference that data.

(each class - histogram etc - is in a different file, and since they
correspond to elements of the GUI, each is a wxPanel or wxFrame)

I think all you need to do is put a reference to the dict of data in
the instances you need them. This is often done in the __init__, by
passing it in, or it could be added later, as in my toy example above.

did that help at all?

If not, perhaps a simpel example we can look at would be easier.

-Chris

···

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (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

Here's my OOP in a Nutshell:

1. Take a few steps back so you can see the "big picture" and think about all the "things" in your program that can be considered some sort of logical unit. These things should be able to be referred to with a noun, for example something like "Person" not "JumpingUpAndDown". The nouns should become the classes.

2. Now think about the relationships between the things that are nouns. If some of them share some attributes but not others, then that might be an opportunity to refactor and add some class inheritance to your collection of things. For example, if you have things that are Circles and things that are Rectangles then you might notice that both of those classes have a position and a color, but only the Circle has a radius and only the Rectangle has width and height. If you then add a Shape class that contains just the position and the color (and any behavior associated with those items) and then have the Circle and Rectangle inherit those attributes and behavior then they don't have to implement it themselves.

3. Next think about the behaviors that each of the nouns have. What can they be told to do? What can they do to themselves? etc. These should all be able to be described as verbs, or action words, and become the methods of the classes. For example, a file object can be told to "open", "close", "read" some bytes, "write" some bytes, etc.

4. In OOP "encapsulation" means, in part, that one part of the program should not be fiddling directly with the attributes or the data of some other object. Instead it should ask the object that owns those attributes or data to do the fiddling for you and perhaps to hand the results of the fiddling back to you.

5. The final step is putting all the pieces together and thinking about the relationships between the nouns. Does an A object contain a B object or perhaps a collection of B's? Are A and B more independent but still communicate one way or both ways? Are A and B totally decoupled but still need to pass messages to each other or react to those messages? The answers to these questions will lead to how and where you create instances of the classes, who owns the references to them, or if you use some loosely coupled message passing system like pubsub.

If you need more then google "introduction to oop python" and "think like a computer scientist python"

···

On 3/28/12 3:25 PM, Carlos Grohmann wrote:

Hi all.

I've beed developing an app for Structural Geology called OpenStereo. It
works, but since I started writing it as I was learning python/wxpython,
and my only programming base prior to that was some Pascal/C, I end up
with a software that lacks the
elegance and advantages od OOP. I really don't get how to use classes in
my app.

Let me explain a little more:

The program must open files with orientation data and it does a lot of
stuff with it. Plots in graphs called stereonets, makes
rose diagrams, histograms etc. Now, I don't know how to use classes, but
from what I could get, once I define something
(a variable) in one class, I can 'broadcast' that value to all parts of
my program, right? So far I use a lot of pubsub and I end up creating
the same data several times (one for the stereonet, another for
histograms�).

So I thought this: when a file is opened (and I must be able to open
several files or even the same file more than once) I store its values
in a variable called 'datasets', which is a dictionary and each entry in
it is a dictionary with all the data from the file, as well as more
stuff (graphic properties etc). But this dictionary must grow/shrink
when I open/delete a file, and I must be able to change some of its
values any time.

So, how to do it?

Should I create a class to hold this variable and then import it in the
others? (each class - histogram etc - is in a different file, and since
they correspond to elements of the GUI, each is a wxPanel or wxFrame)

--
Robin Dunn
Software Craftsman

Now, I don’t know how to use classes, but from what I could get, once I define something (a variable) in one class, I can ‘broadcast’ that value to all parts of my program, right?

The idea of “broadcasting” using, for example, pubsub, is totally distinct from the idea of classes. They are just two different ways of organizing information.

Pubsub uses “listeners” and “senders” to communicate from any point in your program to any other point. Very convenient to allow parts of the program to be independent of each other (which is a good tactic).

Classes create a new “namespace”, a set of variables names that mean something different depending on which class they are from.

So far I use a lot of pubsub and I end up creating the same data several times (one for the stereonet, another for histograms…).

Why? I take it this is repetition you would rather avoid?

But this dictionary must grow/shrink when I open/delete a file, and I must be able to change some of its values any time.

So, how to do it?

As Chris said, dictionaries are made to be changed. Read up on them for how to do that.

Should I create a class to hold this variable and then import it in the others? (each class - histogram etc - is in a different file, and since they correspond to elements of the GUI, each is a wxPanel or wxFrame)

It’s hard to know without knowing more, but perhaps as an example:

I have used a class which in my case is the PlotPanel class, and to which I pass data that it can use to plot some stuff. It has all the methods it needs to create the plots, from data manipulation to GUI stuff. This is, arguably, the “ravioli code” approach, where everything needed is packed into one class. But the downside is, if I ever wanted this to be turned into a web app, I’d have to refactor the code to make it something like Model, View, Controller, where the GUI methods are totally independent of the data manipulating methods. Oh well, perhaps someday.

So, you could have a histogram class, as you mentioned.

Che

When I first started learning Python, I came from a long-time
programming experience in procedural languages. Mark Lutz's book
'Learning Python' taught both Python and OOP in a way which was very
clear to me. I've found that OOP is like an onion in that once you
master one aspect, there are other aspects behind it to learn, but his
book was an excellent starting place for me.

Den

···

On Mar 28, 3:25 pm, Carlos Grohmann <carlos.grohm...@gmail.com> wrote:

Hi all.

I've beed developing an app for Structural Geology called OpenStereo. It works, but since I started writing it as I was learning python/wxpython, and my only programming base prior to that was some Pascal/C, I end up with a software that lacks the
elegance and advantages od OOP. I really don't get how to use classes in my app.<<SNIP>>

I like this video on object oriented programming (although it has some
age and does not talk about the actual programming techniques)

http://video.google.com/videoplay?docid=-4648813441856183405

By Philippe Kahn (Bordland)

Any programming language that supports object oriented programming out
there should have documentation form using objects. Python has it's
own:

···

On 30 mar, 12:24, dncarac <patents...@gmail.com> wrote:

When I first started learning Python, I came from a long-time
programming experience in procedural languages. Mark Lutz's book
'Learning Python' taught both Python and OOP in a way which was very
clear to me. I've found that OOP is like an onion in that once you
master one aspect, there are other aspects behind it to learn, but his
book was an excellent starting place for me.

Den

On Mar 28, 3:25 pm, Carlos Grohmann <carlos.grohm...@gmail.com> wrote:

> Hi all.

> I've beed developing an app for Structural Geology called OpenStereo. It works, but since I started writing it as I was learning python/wxpython, and my only programming base prior to that was some Pascal/C, I end up with a software that lacks the
> elegance and advantages od OOP. I really don't get how to use classes in my app.<<SNIP>>

Alan Etkin wrote:

I like this video on object oriented programming (although it has some
age and does not talk about the actual programming techniques)

http://video.google.com/videoplay?docid=-4648813441856183405

By Philippe Kahn (Bordland)

Borland was an amazing company. I had been a C++ programmer for many
years when I encountered Delphi, but something in their approach to
object-orientation clicked with me. I was a raving loony Delphi fan for
many years -- until I encountered Python. My experience with Delphi
made me a far better C++ programmer.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Carlos, I'm a novice also. My approach to Python + wxPython + "my
application of data" is to break things down like this:

I know you know this part but:
OOP Object Oriented Programming: means to put things into objects
(things) and in those objects also have how they do stuff (verbs).
Objects often interact with each other by accessing each other's
methods.

The things in objects like 'type of sedimentry' are object attributes
and the ways to access and manipulate those attributes are methods.
There are a couple types of methods. Some methods are meant only for
the thing itself to use, other methods are for outside objects to
call. The methods designed to be accessed from other objects is the
objects API, the interface into that object. Sound's like you just
need to grasp a well defined Structural Geology Model API.

So when you want to make an application of your data designed with OOP
you need to break down your applications things into seperate -groups-
of objects.

I'd make a group of objects (classes) for my data, and a seperate
group of objects for the graphical user interface (GUI), which means
wxPython widgets. For the most part I'd try to keep each -group-
naieve of the other group with as little exceptions as possible. The
exceptions will be your OpenStereo application's API.

(I am NOT any kind of authorative on these terms btw.)

I'd link these two groups of objects together with in an Application
Programming Interface (API) which is to say: the data model objects
have generic data methods to access data, and the gui widgets objects
have gui methods to do GUi related things only. And the "Application",
use this word as verb here, is the API for data methods to talk to gui
methods and gui methods to talk with data methods.

Don't confuse the API with the GUI; the gui is for the end-user. The
API is how a programmer interfaces data and how the programmer
interfaces with the GUI library. The programmer makes a NEW API for
his/her application. Your API will be the "OpenStereo" API, which is
part wx library, part data read/write functions, part charting
functions and currently part pubsub. I'll show you another API, that's
not to say a better one, just a different way to have data and gui
interact with each other.

Data is the "Model", an OOP term. The model is your data laid out in
some structure that makes sense only from a data perspective, with
little regard to the Graphical User Interface part, ie the GUI
objects. And you have ways to access that structure; sometimes
programmers call them "accessors" and sometimes they call the way to
access your data as "methods" (of a class).

I'd make a class for each data type. I'm sure you already have this
done. As you said you had a working app.

To give you a visual for what I mean; I've made some untested code.
I'm assuming you know all the stuff below, just haven't seen the GUI
peice, also called the "View" and the data piece, also called the
Model, seperated but linked.

# #####################################################
# geo_models.py

# in case the order of data in your files is relavant
import collections

class NameSpace(object):
  '''Same as a module global namespace, just less abstract (to me),
less cluttered with non-relavant objects.'''

  pass

class SedimentrySample(object):
  '''A data model:
    This class has nothing to do with the GUI,
    only individual items of data.'''

  sample_id = None
  mass = None
  density = None
  volume = None

  def __init__(self, sample_id, mass, density, volume):
    self.sample_id = sample_id
    self.mass = mass
    self.density = density
    self.volume = volume

class SedimentryDataSet(object):
  '''A data model:
    Non-GUI, dataset-only object/class.
    This class is only a -set- of data.
    Later you'll use this set to format your charts.'''

  filename = None
  samples = None
  raw_data = None

  def __init__(self, filename):
    self.filename = filename
    self.samples = collections.OrderedDict()
    self.LoadFromFile( filename )
    self.ParseDataIntoSedimentryObjects()

  def LoadFromFile(self, filename):
    with open(filename, 'r') as filedata:
      self.raw_data = filedata.read()
    self.ParseDataIntoSedimentryObjects()

  def ParseDataIntoSedimentryObjects(self):
    for line in self.raw_data:
      # use your actual function on next line
      sample = SedimentrySample(*line)
      self.samples[sample_id] = sample

class Stereonet(object):
  '''A data model:
    pre GUI thing that can help with the API part later.'''

  filename = None
  chart_labels = None
  chart_increments = None
  data = None # plain data, not raw file data
        # nor formatted for any specific chart layout

  def __init__(self, filename, chart_labels,
      chart_increments):

    self.filename = filename
    self.chart_labels = chart_labels
    self.chart_increments = chart_increments
    self.data = SedimentryDataSet( filename )

class RoseDiagram(Stereonet):
  '''A data model:
    inheret from Stereonet;
    makes a consistanct DATA to GUI ->API<-'''

  chart_data = None # data foramtted specifically for RoseDiagram

  def __init__(self, filename, chart_labels,
      chart_increments):

    super(RoseDiagram, self).__init__(
      filename = filename,
      chart_labels = chart_labels,
      chart_increments = chart_increments )

  def CreateChartData(self):
    '''A non-gui data-centric method'''
    # note that self.data is inherited from class Stereonet()
    self.chart_data = YourFunctionToSetUpChartData(self.data)

class Histogram(Stereonet): pass # you get the idea

# #####################################################
# wxGeoGUI.py

import sys
import wx
import geo_models

# These are the GUI Objects.
# Try to keep data out of their design as much as possible.

YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS = False

class RoseDiagram(wx.Panel):
  '''A GUI object'''

  def __init__(self, parent, filename=None):
    wx.Panel.__init__(self, parent)

    # !! --Instead of pubsub you could do: -- !!

    app = wx.GetApp()
    if YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS:
      self.chart_data = app.data.chart_data.rose_diagram
    else:
      self.chart_data = app.GetRoseDiagram(filename)

    self.DrawChart()

  def DrawChart(self):
    '''Do your drawing of chart_data here.'''
    for sample in self.chart_data.items():
      print('Write some code to draw it.')

class HistDiagram(wx.Panel): pass # similar to class RoseDiagram(wx.Panel)
# complete class definition as an exercise for the programmer

class StereonetPanel(wx.Panel):
  '''A GUI object.'''
  def __init__(self, parent):
    wx.Panel.__init__(self, parent)
    self.chartContainerPanel = wx.Panel(self, size=(500,500))
    self.roseButton = wx.Button(self, label='RoseDiagram')
    self.histButton = wx.Button(self, label='Histogram')
    self.Place(self.chartContainerPanel,
      self.roseButton self.histButton)
    self.Bind(wx.EVT_BUTTON, self.OnRoseClick, self.roseButton)
    self.Bind(wx.EVT_BUTTON, self.OnHistClick, self.histButton)

  def Place(self, childWidgets):
    # a GUI method
    '''Tou code with sizers and stuff to layout your widgets.'''
    pass

  def OnRoseClick(self, event):
    self.chartPanel = RoseDiagram(
      self.chartContainerPanel, filename)
    # event.Skip()

  def OnHistClick(self, event):
    self.chartPanel = HistDiagram(
      self.chartContainerPanel, filename)
    # event.Skip()
    
'''
Instead of using pubsub, sometimes THIS is where I link data and gui;
in the APP object; to me the "app" (application) is one part a view,
i.e. the gui, and the other part of the "app" is the data.
'''

class OpenStereoApp(wx.App):

  '''This gui object links with the non-gui objects by making
  the "Model" an attribute of the GUI object itself.'''

  def OnInit(self, *args, **kwargs):

    '''Instead of pubsub, this is usable;
      although not as thread safe.'''

    self.filename = sys.argv[1]
    self.data = geo_models.Namespace()
    self.data.chart_data = geo_models.Namespace()

    # Here I am showing your that if your application's data model
    # is complex, meaning lots of different data types, then you
    # could break the "data" namespace into smaller namespaces.

···

#
    # A "namespace" has "scope"; those terms are almost synomous.
    #
    # Although not shown here; you could flatten the data by having
    # more then one app.data attribute, like app.chart_data,
    # app.gui_settings, app.defaults, etc.
    #
    # You've been using pubsub to handle your "data access"
    # scoping issues it seems from your question. This is not a bad
    # thing. But you usually choose that approach for well defined
    # reasons. Pubsub, publisher (of events/messages) is usually not
    # directly intended for organizing data access. Although it can be.
    # pubsub is more a messaging system; ie when things happen.

    self.data.chart_data.rose_diagram = None
    self.data.chart_data.hist_diagram = None

    # -- Method One
    self.LoadData( self.filename )

    self.MainFrame = wx.Frame(None, title='OpenStereo Demo OOP App')
    self.MainFrame.MainPanel = StereonetPanel( self.MainFrame )
    self.MainLoop()

  # -- Method One
  def LoadData(self, filename):
    self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )
    self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )

  # -- Method two
  def GetRoseDiagramData(self, filename):
    '''Other GUI widgets call this method to access the data Model.'''
    if not filename: filename = self.filename
    self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )
    return self.data.chart_data.rose_diagram

  def GetHistDiagramData(self, filename):
    '''Other GUI widgets call this method to access the data Model.'''
    if not filename: filename = self.filename
    self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )
    return self.data.chart_data.hist_diagram

if __name__ == '__main__':
  app = OpenStereoApp(0)

BTW; when OOPing, you will greatly benefit from knowing about OOP
Patterns. Some people try to use the MVC or MVP or Observer OOP
patterns. I keep falling back into the wxPython Demo structure of
wxPython. Seems easier to follow.

ok ran out of time. Sorry for anything missing. btw the code above was
not tested.

Many thanks Dev Player! (sorry, didn’t got your name)

I will look deeply into your suggestions, I’m sure I will learn a lot from them.

best

Carlos

···

Prof. Carlos Henrique Grohmann
Institute of Geosciences - Univ. of São Paulo, Brazil

  • Digital Terrain Analysis | GIS | Remote Sensing -

http://carlosgrohmann.com


Can’t stop the signal.

Sent with Sparrow

On Friday, 27 April 2012 at 15:48, Dev Player wrote:

Carlos, I’m a novice also. My approach to Python + wxPython + "my

application of data" is to break things down like this:

I know you know this part but:

OOP Object Oriented Programming: means to put things into objects

(things) and in those objects also have how they do stuff (verbs).

Objects often interact with each other by accessing each other’s

methods.

The things in objects like ‘type of sedimentry’ are object attributes

and the ways to access and manipulate those attributes are methods.

There are a couple types of methods. Some methods are meant only for

the thing itself to use, other methods are for outside objects to

call. The methods designed to be accessed from other objects is the

objects API, the interface into that object. Sound’s like you just

need to grasp a well defined Structural Geology Model API.

So when you want to make an application of your data designed with OOP

you need to break down your applications things into seperate -groups-

of objects.

I’d make a group of objects (classes) for my data, and a seperate

group of objects for the graphical user interface (GUI), which means

wxPython widgets. For the most part I’d try to keep each -group-

naieve of the other group with as little exceptions as possible. The

exceptions will be your OpenStereo application’s API.

(I am NOT any kind of authorative on these terms btw.)

I’d link these two groups of objects together with in an Application

Programming Interface (API) which is to say: the data model objects

have generic data methods to access data, and the gui widgets objects

have gui methods to do GUi related things only. And the “Application”,

use this word as verb here, is the API for data methods to talk to gui

methods and gui methods to talk with data methods.

Don’t confuse the API with the GUI; the gui is for the end-user. The

API is how a programmer interfaces data and how the programmer

interfaces with the GUI library. The programmer makes a NEW API for

his/her application. Your API will be the “OpenStereo” API, which is

part wx library, part data read/write functions, part charting

functions and currently part pubsub. I’ll show you another API, that’s

not to say a better one, just a different way to have data and gui

interact with each other.

Data is the “Model”, an OOP term. The model is your data laid out in

some structure that makes sense only from a data perspective, with

little regard to the Graphical User Interface part, ie the GUI

objects. And you have ways to access that structure; sometimes

programmers call them “accessors” and sometimes they call the way to

access your data as “methods” (of a class).

I’d make a class for each data type. I’m sure you already have this

done. As you said you had a working app.

To give you a visual for what I mean; I’ve made some untested code.

I’m assuming you know all the stuff below, just haven’t seen the GUI

peice, also called the “View” and the data piece, also called the

Model, seperated but linked.

geo_models.py

in case the order of data in your files is relavant

import collections

class NameSpace(object):

‘’'Same as a module global namespace, just less abstract (to me),

less cluttered with non-relavant objects.‘’’

pass

class SedimentrySample(object):

‘’'A data model:

This class has nothing to do with the GUI,

only individual items of data.‘’’

sample_id = None

mass = None

density = None

volume = None

def init(self, sample_id, mass, density, volume):

self.sample_id = sample_id

self.mass = mass

self.density = density

self.volume = volume

class SedimentryDataSet(object):

‘’'A data model:

Non-GUI, dataset-only object/class.

This class is only a -set- of data.

Later you’ll use this set to format your charts.‘’’

filename = None

samples = None

raw_data = None

def init(self, filename):

self.filename = filename

self.samples = collections.OrderedDict()

self.LoadFromFile( filename )

self.ParseDataIntoSedimentryObjects()

def LoadFromFile(self, filename):

with open(filename, ‘r’) as filedata:

self.raw_data = filedata.read()

self.ParseDataIntoSedimentryObjects()

def ParseDataIntoSedimentryObjects(self):

for line in self.raw_data:

use your actual function on next line

sample = SedimentrySample(*line)

self.samples[sample_id] = sample

class Stereonet(object):

‘’'A data model:

pre GUI thing that can help with the API part later.‘’’

filename = None

chart_labels = None

chart_increments = None

data = None # plain data, not raw file data

nor formatted for any specific chart layout

def init(self, filename, chart_labels,

chart_increments):

self.filename = filename

self.chart_labels = chart_labels

self.chart_increments = chart_increments

self.data = SedimentryDataSet( filename )

class RoseDiagram(Stereonet):

‘’'A data model:

inheret from Stereonet;

makes a consistanct DATA to GUI ->API<-‘’’

chart_data = None # data foramtted specifically for RoseDiagram

def init(self, filename, chart_labels,

chart_increments):

super(RoseDiagram, self).init(

filename = filename,

chart_labels = chart_labels,

chart_increments = chart_increments )

def CreateChartData(self):

‘’‘A non-gui data-centric method’‘’

note that self.data is inherited from class Stereonet()

self.chart_data = YourFunctionToSetUpChartData(self.data)

class Histogram(Stereonet): pass # you get the idea

wxGeoGUI.py

import sys

import wx

import geo_models

These are the GUI Objects.

Try to keep data out of their design as much as possible.

YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS = False

class RoseDiagram(wx.Panel):

‘’‘A GUI object’‘’

def init(self, parent, filename=None):

wx.Panel.init(self, parent)

!! --Instead of pubsub you could do: – !!

app = wx.GetApp()

if YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS:

self.chart_data = app.data.chart_data.rose_diagram

else:

self.chart_data = app.GetRoseDiagram(filename)

self.DrawChart()

def DrawChart(self):

‘’‘Do your drawing of chart_data here.’‘’

for sample in self.chart_data.items():

print(‘Write some code to draw it.’)

class HistDiagram(wx.Panel): pass # similar to class RoseDiagram(wx.Panel)

complete class definition as an exercise for the programmer

class StereonetPanel(wx.Panel):

‘’‘A GUI object.’‘’

def init(self, parent):

wx.Panel.init(self, parent)

self.chartContainerPanel = wx.Panel(self, size=(500,500))

self.roseButton = wx.Button(self, label=‘RoseDiagram’)

self.histButton = wx.Button(self, label=‘Histogram’)

self.Place(self.chartContainerPanel,

self.roseButton self.histButton)

self.Bind(wx.EVT_BUTTON, self.OnRoseClick, self.roseButton)

self.Bind(wx.EVT_BUTTON, self.OnHistClick, self.histButton)

def Place(self, childWidgets):

a GUI method

‘’‘Tou code with sizers and stuff to layout your widgets.’‘’

pass

def OnRoseClick(self, event):

self.chartPanel = RoseDiagram(

self.chartContainerPanel, filename)

event.Skip()

def OnHistClick(self, event):

self.chartPanel = HistDiagram(

self.chartContainerPanel, filename)

event.Skip()

‘’’

Instead of using pubsub, sometimes THIS is where I link data and gui;

in the APP object; to me the “app” (application) is one part a view,

i.e. the gui, and the other part of the “app” is the data.

‘’’

class OpenStereoApp(wx.App):

‘’'This gui object links with the non-gui objects by making

the “Model” an attribute of the GUI object itself.‘’’

def OnInit(self, *args, **kwargs):

‘’'Instead of pubsub, this is usable;

although not as thread safe.‘’’

self.filename = sys.argv[1]

self.data = geo_models.Namespace()

self.data.chart_data = geo_models.Namespace()

Here I am showing your that if your application’s data model

is complex, meaning lots of different data types, then you

could break the “data” namespace into smaller namespaces.

A “namespace” has “scope”; those terms are almost synomous.

Although not shown here; you could flatten the data by having

more then one app.data attribute, like app.chart_data,

app.gui_settings, app.defaults, etc.

You’ve been using pubsub to handle your “data access”

scoping issues it seems from your question. This is not a bad

thing. But you usually choose that approach for well defined

reasons. Pubsub, publisher (of events/messages) is usually not

directly intended for organizing data access. Although it can be.

pubsub is more a messaging system; ie when things happen.

self.data.chart_data.rose_diagram = None

self.data.chart_data.hist_diagram = None

– Method One

self.LoadData( self.filename )

self.MainFrame = wx.Frame(None, title=‘OpenStereo Demo OOP App’)

self.MainFrame.MainPanel = StereonetPanel( self.MainFrame )

self.MainLoop()

– Method One

def LoadData(self, filename):

self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )

self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )

– Method two

def GetRoseDiagramData(self, filename):

‘’‘Other GUI widgets call this method to access the data Model.’‘’

if not filename: filename = self.filename

self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )

return self.data.chart_data.rose_diagram

def GetHistDiagramData(self, filename):

‘’‘Other GUI widgets call this method to access the data Model.’‘’

if not filename: filename = self.filename

self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )

return self.data.chart_data.hist_diagram

if name == ‘main’:

app = OpenStereoApp(0)

BTW; when OOPing, you will greatly benefit from knowing about OOP

Patterns. Some people try to use the MVC or MVP or Observer OOP

patterns. I keep falling back into the wxPython Demo structure of

wxPython. Seems easier to follow.

ok ran out of time. Sorry for anything missing. btw the code above was

not tested.

To unsubscribe, send email to wxPython-users+unsubscribe@googlegroups.com

or visit http://groups.google.com/group/wxPython-users?hl=en

Hi, Carlos –

I can’t give you the good advice of the others here, but I wanted to tell you I stumbled across Open Stereo a couple of months ago when I was looking for an open source method of drawing stereonets. Whatever the flaws of the program in your eyes, my boss was highly impressed with the plots I gave him. There are some changes I’d like to make to it, and I’ve been studying it as I get time, but as a first attempt I think it was a very commendable project.

So thank you!

Dave

···

On Monday, April 30, 2012 6:57:49 PM UTC-7, Carlos Grohmann wrote:

Many thanks Dev Player! (sorry, didn’t got your name)

I will look deeply into your suggestions, I’m sure I will learn a lot from them.

best

Carlos

Prof. Carlos Henrique Grohmann
Institute of Geosciences - Univ. of São Paulo, Brazil

  • Digital Terrain Analysis | GIS | Remote Sensing -

http://carlosgrohmann.com


Can’t stop the signal.

Sent with Sparrow

On Friday, 27 April 2012 at 15:48, Dev Player wrote:

Carlos, I’m a novice also. My approach to Python + wxPython + "my

application of data" is to break things down like this:

I know you know this part but:

OOP Object Oriented Programming: means to put things into objects

(things) and in those objects also have how they do stuff (verbs).

Objects often interact with each other by accessing each other’s

methods.

The things in objects like ‘type of sedimentry’ are object attributes

and the ways to access and manipulate those attributes are methods.

There are a couple types of methods. Some methods are meant only for

the thing itself to use, other methods are for outside objects to

call. The methods designed to be accessed from other objects is the

objects API, the interface into that object. Sound’s like you just

need to grasp a well defined Structural Geology Model API.

So when you want to make an application of your data designed with OOP

you need to break down your applications things into seperate -groups-

of objects.

I’d make a group of objects (classes) for my data, and a seperate

group of objects for the graphical user interface (GUI), which means

wxPython widgets. For the most part I’d try to keep each -group-

naieve of the other group with as little exceptions as possible. The

exceptions will be your OpenStereo application’s API.

(I am NOT any kind of authorative on these terms btw.)

I’d link these two groups of objects together with in an Application

Programming Interface (API) which is to say: the data model objects

have generic data methods to access data, and the gui widgets objects

have gui methods to do GUi related things only. And the “Application”,

use this word as verb here, is the API for data methods to talk to gui

methods and gui methods to talk with data methods.

Don’t confuse the API with the GUI; the gui is for the end-user. The

API is how a programmer interfaces data and how the programmer

interfaces with the GUI library. The programmer makes a NEW API for

his/her application. Your API will be the “OpenStereo” API, which is

part wx library, part data read/write functions, part charting

functions and currently part pubsub. I’ll show you another API, that’s

not to say a better one, just a different way to have data and gui

interact with each other.

Data is the “Model”, an OOP term. The model is your data laid out in

some structure that makes sense only from a data perspective, with

little regard to the Graphical User Interface part, ie the GUI

objects. And you have ways to access that structure; sometimes

programmers call them “accessors” and sometimes they call the way to

access your data as “methods” (of a class).

I’d make a class for each data type. I’m sure you already have this

done. As you said you had a working app.

To give you a visual for what I mean; I’ve made some untested code.

I’m assuming you know all the stuff below, just haven’t seen the GUI

peice, also called the “View” and the data piece, also called the

Model, seperated but linked.

geo_models.py

in case the order of data in your files is relavant

import collections

class NameSpace(object):

‘’'Same as a module global namespace, just less abstract (to me),

less cluttered with non-relavant objects.‘’’

pass

class SedimentrySample(object):

‘’'A data model:

This class has nothing to do with the GUI,

only individual items of data.‘’’

sample_id = None

mass = None

density = None

volume = None

def init(self, sample_id, mass, density, volume):

self.sample_id = sample_id

self.mass = mass

self.density = density

self.volume = volume

class SedimentryDataSet(object):

‘’'A data model:

Non-GUI, dataset-only object/class.

This class is only a -set- of data.

Later you’ll use this set to format your charts.‘’’

filename = None

samples = None

raw_data = None

def init(self, filename):

self.filename = filename

self.samples = collections.OrderedDict()

self.LoadFromFile( filename )

self.ParseDataIntoSedimentryObjects()

def LoadFromFile(self, filename):

with open(filename, ‘r’) as filedata:

self.raw_data = filedata.read()

self.ParseDataIntoSedimentryObjects()

def ParseDataIntoSedimentryObjects(self):

for line in self.raw_data:

use your actual function on next line

sample = SedimentrySample(*line)

self.samples[sample_id] = sample

class Stereonet(object):

‘’'A data model:

pre GUI thing that can help with the API part later.‘’’

filename = None

chart_labels = None

chart_increments = None

data = None # plain data, not raw file data

nor formatted for any specific chart layout

def init(self, filename, chart_labels,

chart_increments):

self.filename = filename

self.chart_labels = chart_labels

self.chart_increments = chart_increments

self.data = SedimentryDataSet( filename )

class RoseDiagram(Stereonet):

‘’'A data model:

inheret from Stereonet;

makes a consistanct DATA to GUI ->API<-‘’’

chart_data = None # data foramtted specifically for RoseDiagram

def init(self, filename, chart_labels,

chart_increments):

super(RoseDiagram, self).init(

filename = filename,

chart_labels = chart_labels,

chart_increments = chart_increments )

def CreateChartData(self):

‘’‘A non-gui data-centric method’‘’

note that self.data is inherited from class Stereonet()

self.chart_data = YourFunctionToSetUpChartData(self.data)

class Histogram(Stereonet): pass # you get the idea

wxGeoGUI.py

import sys

import wx

import geo_models

These are the GUI Objects.

Try to keep data out of their design as much as possible.

YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS = False

class RoseDiagram(wx.Panel):

‘’‘A GUI object’‘’

def init(self, parent, filename=None):

wx.Panel.init(self, parent)

!! --Instead of pubsub you could do: – !!

app = wx.GetApp()

if YOU_LIKE_DIRECT_ATTRIBUTE_ACCESS:

self.chart_data = app.data.chart_data.rose_diagram

else:

self.chart_data = app.GetRoseDiagram(filename)

self.DrawChart()

def DrawChart(self):

‘’‘Do your drawing of chart_data here.’‘’

for sample in self.chart_data.items():

print(‘Write some code to draw it.’)

class HistDiagram(wx.Panel): pass # similar to class RoseDiagram(wx.Panel)

complete class definition as an exercise for the programmer

class StereonetPanel(wx.Panel):

‘’‘A GUI object.’‘’

def init(self, parent):

wx.Panel.init(self, parent)

self.chartContainerPanel = wx.Panel(self, size=(500,500))

self.roseButton = wx.Button(self, label=‘RoseDiagram’)

self.histButton = wx.Button(self, label=‘Histogram’)

self.Place(self.chartContainerPanel,

self.roseButton self.histButton)

self.Bind(wx.EVT_BUTTON, self.OnRoseClick, self.roseButton)

self.Bind(wx.EVT_BUTTON, self.OnHistClick, self.histButton)

def Place(self, childWidgets):

a GUI method

‘’‘Tou code with sizers and stuff to layout your widgets.’‘’

pass

def OnRoseClick(self, event):

self.chartPanel = RoseDiagram(

self.chartContainerPanel, filename)

event.Skip()

def OnHistClick(self, event):

self.chartPanel = HistDiagram(

self.chartContainerPanel, filename)

event.Skip()

‘’’

Instead of using pubsub, sometimes THIS is where I link data and gui;

in the APP object; to me the “app” (application) is one part a view,

i.e. the gui, and the other part of the “app” is the data.

‘’’

class OpenStereoApp(wx.App):

‘’'This gui object links with the non-gui objects by making

the “Model” an attribute of the GUI object itself.‘’’

def OnInit(self, *args, **kwargs):

‘’'Instead of pubsub, this is usable;

although not as thread safe.‘’’

self.filename = sys.argv[1]

self.data = geo_models.Namespace()

self.data.chart_data = geo_models.Namespace()

Here I am showing your that if your application’s data model

is complex, meaning lots of different data types, then you

could break the “data” namespace into smaller namespaces.

A “namespace” has “scope”; those terms are almost synomous.

Although not shown here; you could flatten the data by having

more then one app.data attribute, like app.chart_data,

app.gui_settings, app.defaults, etc.

You’ve been using pubsub to handle your “data access”

scoping issues it seems from your question. This is not a bad

thing. But you usually choose that approach for well defined

reasons. Pubsub, publisher (of events/messages) is usually not

directly intended for organizing data access. Although it can be.

pubsub is more a messaging system; ie when things happen.

self.data.chart_data.rose_diagram = None

self.data.chart_data.hist_diagram = None

– Method One

self.LoadData( self.filename )

self.MainFrame = wx.Frame(None, title=‘OpenStereo Demo OOP App’)

self.MainFrame.MainPanel = StereonetPanel( self.MainFrame )

self.MainLoop()

– Method One

def LoadData(self, filename):

self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )

self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )

– Method two

def GetRoseDiagramData(self, filename):

‘’‘Other GUI widgets call this method to access the data Model.’‘’

if not filename: filename = self.filename

self.data.chart_data.rose_diagram = geo_models.RoseDiagram( filename )

return self.data.chart_data.rose_diagram

def GetHistDiagramData(self, filename):

‘’‘Other GUI widgets call this method to access the data Model.’‘’

if not filename: filename = self.filename

self.data.chart_data.hist_diagram = geo_models.HistDiagram( filename )

return self.data.chart_data.hist_diagram

if name == ‘main’:

app = OpenStereoApp(0)

BTW; when OOPing, you will greatly benefit from knowing about OOP

Patterns. Some people try to use the MVC or MVP or Observer OOP

patterns. I keep falling back into the wxPython Demo structure of

wxPython. Seems easier to follow.

ok ran out of time. Sorry for anything missing. btw the code above was

not tested.

To unsubscribe, send email to wxPython-users+unsubscribe@googlegroups.com

or visit http://groups.google.com/group/wxPython-users?hl=en