oo problem

T

Tool69

Hi,
I've got a simple but difficult problem :

Suppose I've got a Paper class, on wich I can draw i.e a rectangle, a
circle or whatever.

class Paper(...):
def __init__(self, paperx, papery):
self.paperx = paperx
self.papery = papery
....
def draw(self, Primitive_Object):
....
class Rectangle( ): <--- a Primitive_Object
...

Now, inside my Rectangle class, I need to recover the Paper instance
who called it (because I need the paper sizes to calculate something).
I now I can use a global variable, say " _paper" and then, inside my
Paper.__init__() write something like this :

global _paper
_paper = [paperx,papery]

But it has drawbacks : what if I've got several Paper instances ?

I don't know if I'm clear enought,
Thanks for your help :
6TooL9
 
?

=?ISO-8859-1?Q?Nils_Oliver_Kr=F6ger?=

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

many class libraries use a parent attribute for this purpose:

class Rectangle (object):
__init__(self, parent):
#Use the parent as "private": nobody except your own #class should
mess around with it ...
self._parent = parent

The parent of course needs to be an existing Paper instance.

You might check whether the given value for parent is indeed a Paper
instance in your Rectangle's __init__, otherwise you might get strange
AttributeError exceptions when using the parent later.

Hope that helps!

Nils


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFFe+SyRQeuB6ws8wERAuTOAKCPKe8m9BNP7Vf/aFpJSWTfOkF8pQCgvkxG
+xmcU+UB7fT0fi8/Jz+o15E=
=pW+u
-----END PGP SIGNATURE-----
 
K

Kay Schluehr

Tool69 said:
Hi,
I've got a simple but difficult problem :

Suppose I've got a Paper class, on wich I can draw i.e a rectangle, a
circle or whatever.

class Paper(...):
def __init__(self, paperx, papery):
self.paperx = paperx
self.papery = papery
....
def draw(self, Primitive_Object):
....
class Rectangle( ): <--- a Primitive_Object
...

Now, inside my Rectangle class, I need to recover the Paper instance
who called it (because I need the paper sizes to calculate something).

You can simply pass a paper reference to a rectangle object. But better
you pass only the information to the rectangle that is necessary,
because you decouple the objects this way:

class Rectangle:
def __init__(self):
self.paper_coordinates = [0,0] # default value

class Paper:
...
def draw(self, shape):
shape.paper_coordinates = self.x, self.y
shape.draw()
...
 
T

Tool69

Thanks for your answers,
I though about the first solution too, but I've redundant code, say ie:
p = Paper(100,200)
p.draw( Rectangle(p,10,20,50,60) ) <-- we're forced to write 'p' two
times.

But that maybe the best solution, as I cannot see anything else.

For the second one, I need to make some calculus on the shape to be
drawn, so if I write:

shape.paper_coordinates = self.x, self.y
shape.draw()

It's too late, the calculus have already been done with the default
values, right or not?

Thanks,
6TooL9
 
J

James Stroud

Tool69 said:
Hi,
I've got a simple but difficult problem :

Suppose I've got a Paper class, on wich I can draw i.e a rectangle, a
circle or whatever.

class Paper(...):
def __init__(self, paperx, papery):
self.paperx = paperx
self.papery = papery
....
def draw(self, Primitive_Object):
....
class Rectangle( ): <--- a Primitive_Object
...

Now, inside my Rectangle class, I need to recover the Paper instance
who called it (because I need the paper sizes to calculate something).
I now I can use a global variable, say " _paper" and then, inside my
Paper.__init__() write something like this :

global _paper
_paper = [paperx,papery]

But it has drawbacks : what if I've got several Paper instances ?

I don't know if I'm clear enought,
Thanks for your help :
6TooL9

I think you may not be thinking about your problem in the most
object-oriented way. For example, the Paper.draw() method will already
have a reference to the "calling" Paper instance accessible via the name
"self". E.g.:

class Paper(...
def draw(self, primitive):
do_something_with(self.paperx, self.papery)
# etc

Now, if the draw() method were in the primitive and not the paper, then
a reference to the paper should be kept in the primitive. But, as you
will see below, this seems to make little sense (to me, at least).

If you find that you need to access the primitive's "parent" paper
attributes from within the primitive, then you might want to check
whether you have structured your code in the most sensible manner.
Conceptually, children shouldn't be overly concerned with the workings
of their parents, lest they be a bit too presumptuous and risk a good
scolding.

The thought process I always go through is "why does this object need to
have this particular reference?" Do primitives really need to know
anything about the paper on which they are drawn?

I try not to make objects overly reflexive. For example, should a
primitive really be drawing itself? Perhaps something else should bring
a primitive into existence based on the attributes contained in a
primitive's data structure and the properties of the medium in which or
on which it will exist.

What if some creator had asked of you: "Embed Thyself unto the 3-D
Manifold Known as the Universe." Your first requirement before any steps
toward physical existence would, of course, be a reference to the
Universe--so that you could see if you would actually fit.

Perhaps it would be better for a 3rd party to manage the process of
person creation, like, say, a mother? Children would then not be
required to ponder the extent of the Universe before their own physical
manifestation and could focus on things more meaningful to themselves,
like whether they are hungry or need to go potty.

By this thought process, I propose that it makes more sense to create a
Drawing object that has references to both the primitives and the paper
and then manage drawing through that object.

class Drawing(object):
def __init__(self, paper, primitives):
self._paper = paper
self._primitives = primitives

my_primitives = [Square(5), Circle(10), DoubleEndedArrow(5,5,5,1,2,1,2)]
my_paper = Paper()
my_drawing = Drawing(my_paper, my_primitives)

Now the paper can just worry about its paper specific attributes (which
might be its dimensions, border, bond weight, etc), the primitives can
worry about primitive specific attributes (size, line-width,
fill-color), and the drawing can manage everything required to make a
drawing (paper, primitives, color model, etc.).

I think that this is the what you were getting at with your global
"_paper" variable, except now "global" is the context of the Drawing class.

James
 
T

Tool69

Thanks James,

It was a matter of choice : in my earlier version, all primitives know
how to draw themselves, but they needed to have a parent and I found it
was not good for aesthetic reasons.

but I think I've been misunderstood. Let's take a concrete sample :

I've got a Paper instance(-5,5,5,5) (the coordinates of lower-left and
upper-right rectangle: xlowleft, ylowlef, xupright, yupright) of my
paper.
I want now to draw an x-axis on it with some ticks and maybe lables, I
need to catch the xlowleft and xupright variables of the Paper inside
my primitive, ok ?
The problem lies inside my primitive, not on the draw method.
Here's a simplified example, I need to retrieve paper.xll and
paper.xur:

class Graduate_x( primitive ):
def __init__(self, orient = 'se', xorigin = 0, yorigin=0, xunit=
1.0, yunit=1.0):
primitive.__init__(self)
for k in range( int(math.floor(( paper.xll - xorigin)/xun)),
int(math.floor((paper.xur-xorigin)/yun)) ):
...something to construct my path
 
?

=?ISO-8859-1?Q?Nils_Oliver_Kr=F6ger?=

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
It was a matter of choice : in my earlier version, all primitives know
how to draw themselves, but they needed to have a parent and I found it
was not good for aesthetic reasons.

Why should a parent reference not be aesthetic? OOD is all about Objects
and their relationships.

I want now to draw an x-axis on it with some ticks and maybe lables, I
need to catch the xlowleft and xupright variables of the Paper inside
my primitive, ok ?

This sentence describes the relationship between the paper and your
Rectangle (to stick to your first example) as an association:

The Rectangle needs to access information from the Paper. Moreover, the
relationship could be named "Rectangle lies on Paper" this is perfect
OOD in my opinion.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFFfGPmRQeuB6ws8wERAgBgAKCiNlNU5vuVmUQQWy/Trqv2/hfd2gCg39Sh
WC2+yoWA+lpJ4PNQ1sfiFTU=
=UIww
-----END PGP SIGNATURE-----
 
J

James Stroud

Tool69 said:
Thanks James,

It was a matter of choice : in my earlier version, all primitives know
how to draw themselves, but they needed to have a parent and I found it
was not good for aesthetic reasons.

but I think I've been misunderstood. Let's take a concrete sample :

I've got a Paper instance(-5,5,5,5) (the coordinates of lower-left and
upper-right rectangle: xlowleft, ylowlef, xupright, yupright) of my
paper.
I want now to draw an x-axis on it with some ticks and maybe lables, I
need to catch the xlowleft and xupright variables of the Paper inside
my primitive, ok ?
The problem lies inside my primitive, not on the draw method.
Here's a simplified example, I need to retrieve paper.xll and
paper.xur:

class Graduate_x( primitive ):
def __init__(self, orient = 'se', xorigin = 0, yorigin=0, xunit=
1.0, yunit=1.0):
primitive.__init__(self)
for k in range( int(math.floor(( paper.xll - xorigin)/xun)),
int(math.floor((paper.xur-xorigin)/yun)) ):
...something to construct my path

You can simply pass the paper to the __init__ method and even store a
reference to the paper there if you have to re-construct the path later,
say if the paper size changes or the origins of the primitive change. I
wouldn't advise storing references to the primitives in the paper as
well, because you would get circular references.

Global variables are probably not the way to handle this. For OO, if you
feel like you need a global variable, try to think up an object that can
sensibly contain all the objects that would be global.

James
 
D

Dennis Lee Bieber

Thanks for your answers,
I though about the first solution too, but I've redundant code, say ie:
p = Paper(100,200)
p.draw( Rectangle(p,10,20,50,60) ) <-- we're forced to write 'p' two
times.

I'd suggest not passing an instance initialization to your "Paper"
draw() method...

p = Paper(100, 200)
p.add(Rectangle(10, 20, 50, 60))
p.add(OtherPrimitives())
p.draw()

where p.draw() (Paper.draw(self) really) does something like:

self.whatever_is_needed_for_Paper_itself
for prm = self.primitives: #list of stuff from p.add(...) calls
prm.draw(self) #pass current paper to the primitive
#which draws itself on the paper
#and can do whatever calculations
#(CLIPPING perhaps) needed

--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
T

Tool69

Dennis Lee Bieber a écrit :
I'd suggest not passing an instance initialization to your "Paper"
draw() method...

p = Paper(100, 200)
p.add(Rectangle(10, 20, 50, 60))
p.add(OtherPrimitives())
p.draw()

where p.draw() (Paper.draw(self) really) does something like:

self.whatever_is_needed_for_Paper_itself
for prm = self.primitives: #list of stuff from p.add(...) calls
prm.draw(self) #pass current paper to the primitive
#which draws itself on the paper
#and can do whatever calculations
#(CLIPPING perhaps) needed

Thanks for all your comments and suggestions.
Denis, I would like to know how I can made a draw method for a
primitive without a Paper instance inside ...that's always the same
problem as only my Paper instance knows the Paper sizes !
 
D

Dennis Lee Bieber

Dennis Lee Bieber a écrit :


Thanks for all your comments and suggestions.
Denis, I would like to know how I can made a draw method for a
primitive without a Paper instance inside ...that's always the same
problem as only my Paper instance knows the Paper sizes !

Perhaps you missed that the loop (in Paper) that invokes each
primitive's draw() is passing itself (the Paper instance)...

The signature for primitives would look like:

def draw(self, parent=None):
# do something with the primitive on the parent object

So in the loop

prm.draw(self) => primitive.draw(prm, self)

where "self" is the Paper instance that is doing the call.
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
T

Tool69

Dennis Lee Bieber a écrit :
Perhaps you missed that the loop (in Paper) that invokes each
primitive's draw() is passing itself (the Paper instance)...

Oops, sorry I missed it in fact.

But I still have a problem with my the primitives (some pathes I had to
build ):
i.e a mathematical ray is infinite, so I must draw a segment on screen
and need the paper sizes to calculate the segments starts and ends (
I've got a lot of objects like this ). I don't know how to build them
without passing a paper instance.
The actual implementation of some primitives uses :

def __init__(self, paper_instance, etc. ):
do something

But I as mentioned before, I'm not satisfied with it since I shall
write this do draw a Line :*
paper.draw( Line( paper, x1, y1, x2, y2) This is what I don't find
aesthetic : the repetition of "paper" calls.
 
B

Bruno Desthuilliers

Tool69 a écrit :
Hi,
I've got a simple but difficult problem :

Suppose I've got a Paper class, on wich I can draw i.e a rectangle, a
circle or whatever.

class Paper(...):
def __init__(self, paperx, papery):
self.paperx = paperx
self.papery = papery
....
def draw(self, Primitive_Object):
....
class Rectangle( ): <--- a Primitive_Object
...

Now, inside my Rectangle class, I need to recover the Paper instance
who called it (because I need the paper sizes to calculate something).
I now I can use a global variable, say " _paper" and then, inside my
Paper.__init__() write something like this :

global _paper
_paper = [paperx,papery]

But it has drawbacks :
Indeed...

what if I've got several Paper instances ?

class Paper(...):
def draw(self, drawable):
drawable.draw(self)

class Rectangle( ): <--- a drawable
...
def draw(self, drawing_surface):
...

Now consider whether you really need Paper.draw...

HTH
 
D

Dennis Lee Bieber

NOTE: the original response was done via email from a location where I
do not have posting privileges; this is a copy of that message for
continuity...

Dennis Lee Bieber a écrit :


Oops, sorry I missed it in fact.

But I still have a problem with my the primitives (some pathes I had to
build ):
i.e a mathematical ray is infinite, so I must draw a segment on screen
and need the paper sizes to calculate the segments starts and ends (
I've got a lot of objects like this ). I don't know how to build them
without passing a paper instance.
The actual implementation of some primitives uses :

def __init__(self, paper_instance, etc. ):
do something

But I as mentioned before, I'm not satisfied with it since I shall
write this do draw a Line :*
paper.draw( Line( paper, x1, y1, x2, y2) This is what I don't find
aesthetic : the repetition of "paper" calls.

This is, in my mind, both a usage and a design flaw.

You are creating (and throwing away) instances of drawable
objects to the draw method of a paper instance. But what does
paper.draw() actually do with the drawable object? Call a draw method
within it? If so, that is when you should pass the paper instance (or
just the part needed -- clipping rectangle perhaps?).

Without any actual drawing operations, see if you can follow
along with this code.

-=-=-=-=-=-=-
class Drawable(object):
def __init__(self):
raise NotImplementedError
def draw(self, parent=None):
if not parent: #note: I've not defined the exceptions
raise DrawableError("Must specify parent canvas")

class Point(Drawable):
def __init__(self, x=0, y=0):
self.x = x
self.y = y

class Line(Drawable):
def __init__(self, start, end):
self._start = start
self._end = end
def draw(self, parent=None):
super(Line, self).draw(parent)
# perform whatever is needed to draw /this/ object
# onto the parent (canvas) object.

class Rectangle(Drawable):
def __init__(self, topLeft, bottomRight):
self._topLeft = topLeft
self._bottomRight = bottomRight
self._topRight = Point(bottomRight.x, topLeft.y)
self._bottomLeft = Point(topLeft.x, bottomRight.y)
def draw(self, parent=None):
super(Rectangle, self).draw(parent)
# perform whatever is needed to draw /this/ object
# onto the parent (canvas) object

# etc. for other primitive drawing objects

class Canvas(object):
def __init__(self):
self.clear()
def draw(self):
for p in self._displayList:
p.draw(self)
def getDisplayList(self):
return self._displayList[:] #copy of list
def clear(self):
self._displayList = []
def add(self, primitive=None):
if primitive:
self._displayList.append(primitive)

class Paper(Canvas):
def __init__(self, width, height):
super(Paper, self).__init__()
self._width = width
self._height = height
# don't need to define draw(), getDisplayList(), clear() or add()
# as those of Canvas will be used as is. Only if Paper-specific
# options are needed do they need overrides similar to the
# __init__() difference.

# other canvas types could go here
# with some work, even the canvas could be a "drawable"

##
## usage!
##

p = Paper(8.5, 11) #create paper (US Letter if using inches)

p.add(Line(Point(3, 3), Point(10, 10))) # add a line (note clipping!)
p.add(Rectangle(Point(3.14159, 2.787), Point(2.787, 3.14159)))

p.draw() #draw ALL primitives at once.

p.add(Rectangle(Point(-5, 5), Point(5, -5)))

p.draw() #redraw with newly added primitive

p2 = Paper(6, 4) #create a postcard size page

for primitive in p.getDisplayList():
p2.add(primitive) #copy original drawables to new paper

p2.draw() #draw them clipped to the new page size

Line(Point(x1, y1), Point(x2, y2)).draw(p2)
#draws an anonymous, throw-away, line on p2 (it is not
#added to the display list, so a subsequent p2.draw()
#will not show /this/ line
-=-=-=-=-=-=-

The actual implementation of draw() for each primitive will have
to handle clipping to the boundaries of the Canvas object that is passed
to it.

You'll notice that the only place the primitive needs to know
about the canvas is in its specific draw method. And only at that time
is the canvas (paper) passed to it.

Instead of the concept;

Paper, draw a line from x to y

(and having to pass the "paper" to the initializer of the line
primitive), you have to think in terms of:

Line, draw yourself on this paper

Or, if you consider the last example above… Compare that to your
example:

-=-=-=-=-
paper.draw( Line( paper, x1, y1, x2, y2) )
-=-=-=-=-=-

Here, you are telling the paper to do the drawing, and passing
it a Line instance (and passing it the paper it is supposed to be drawn
on). Why? The paper isn't drawing the line on itself… While

-=-=-=-=-
Line(Point(x1, y1), Point(x2, y2)).draw(paper)
-=-=-=-=-

initializes a Line instance, then asks it to draw itself using paper as
the surface to be drawn upon.
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
T

tool69

Hi,
First, let me thanks you for all your clear comments.

This is, in my mind, both a usage and a design flaw.

You are creating (and throwing away) instances of drawable
objects to the draw method of a paper instance. But what does
paper.draw() actually do with the drawable object? Call a draw method
within it?

No, actually the paper instance is a subclass of a canvas from an
external module. And this one have a stroke() method.
In reading your comments, I think it's now clear that I must get back
and let any primitive have an inner draw() method ( a subclass of
Drawable object in our case).
If so, that is when you should pass the paper instance (or
just the part needed -- clipping rectangle perhaps?).

In fact, my approach was a bad one : in initialising the paper instance,
I was already clipping it.
If I wrote : p = Paper(-5,-5,5,5), all drawings made on that paper will
be clipped inside a rectangle with lower-left corner (-5,-5) and
upper-right corner (5,5).
Now, I think it's better to clip after all primitives have been added to
the paper.
The actual implementation of draw() for each primitive will have
to handle clipping to the boundaries of the Canvas object that is passed
to it.

You'll notice that the only place the primitive needs to know
about the canvas is in its specific draw method. And only at that time
is the canvas (paper) passed to it.

Instead of the concept;

Paper, draw a line from x to y

(and having to pass the "paper" to the initializer of the line
primitive), you have to think in terms of:

Line, draw yourself on this paper

Or, if you consider the last example above… Compare that to your
example:

-=-=-=-=-
paper.draw( Line( paper, x1, y1, x2, y2) )
-=-=-=-=-=-

Here, you are telling the paper to do the drawing, and passing
it a Line instance (and passing it the paper it is supposed to be drawn
on). Why? The paper isn't drawing the line on itself… While

-=-=-=-=-
Line(Point(x1, y1), Point(x2, y2)).draw(paper)
-=-=-=-=-

initializes a Line instance, then asks it to draw itself using paper as
the surface to be drawn upon.

Yes I was wrong, that's all clear now.
Thanks again, this was very helpfull.

6TooL9
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,252
Latest member
MeredithPl

Latest Threads

Top