passing rectangle objects through function - very odd behavior

I was using PseudoDC and moving objects around and kept
getting errors in rectangles calculation inside of methods. I built a
simpler app to reproduce the problem. In this app, there’s no
PseudoDC and I don’t draw the rectangle. I have only a text control
to display the rectangles coordinates. I create the rectangle in a
separate class, then translate it as if it had been dragged. I then take
the union of the original rectangle with the new rectangle, and inflate
this by 5. This gives me the area that would need to be refreshed (if I
was drawing to the canvas).

The problem is when the “translate” method
returns the inflated calculated refresh rectangle. If I assign the
inflated rectangle to a new variable, and return that, the calling statement
receives an error on the first coordinate. But if I take a copy of the rectangle
object and return that there is no error. Here is the code that works.
See below for an explanation of the weird behavior:

import wx

import copy

class frame(wx.Frame):

def __init__(self):

    wx.Frame.__init__(self,

None, title=“test move”,size=(400,400))

    #text control is

used to capture a click, and display results

self.editTxt=wx.TextCtrl(self, wx.ID_ANY, “text”,pos=(0,0),
style=wx.TE_MULTILINE)

self.editTxt.Bind(wx.EVT_LEFT_DOWN, self.OnMouse)

    #subObj is a

class holding a rectangle (wx.Rect) only for now

self.p=subObj(100,100,50,50)

def OnMouse(self,event):

rU=self.p.translate(5,10)

self.editTxt.Clear()

self.editTxt.WriteText("subObj rect= " + str(self.p.r) +
“\n” +

"calc rect = " +str(rU) + “\n”)

class subObj(object):

def __init__(self,x,y,width,height):

    self.r =

wx.Rect(x,y,width,height)

def translate(self,dx,dy):

“”"

    This simulate

dragging (without displaying it). It returns the area that would need
refreshing,

    by taking the

union of the original and the displace rectangle"""

    r0 =

copy.copy(self.r) #remember the original rectangle

self.r.OffsetXY(dx,dy) #move the class rectangle (by
x=5 and y=10)

r0.Union(self.r) #
union of two rectangle, r0 is modified

    rUpdate=

r0.Inflate(5,5) # inflate the resulting rectangle by 5 all around,
r0 is modified

    # at this point

rUpdate reads rect = (95,95, 65,70)

rUpdateC=copy.copy(rUpdate)

    return rUpdateC

class App(wx.App):

def OnInit(self):

    myFrame=frame()

    myFrame.Show()

    return True

if name == ‘main’:

app = App()

app.MainLoop()

**************EXPLANATION:

Look at the translate method is class subObj:

As written, it return a rectangle = (95,95,65.70) which is
correct.

If I leave the code as is (i.e I still take a copy of
rUpdate), but I return rUpdate instead of its copy(rUpdateC),

the code returns and error: rectangle = (0,95,65,70). The
first coordinates is wrong.

Wait it gets better:

If I comment out the copy statement, and return rUpdate, I
get rectangle = (30013472,95,65,70) – first coordinate is very wrong.

Finally, if I run this last version outside of the debugger
(I use Wing), I get a different large number for the first coordinate.

Can somebody explain to me what is happening? I
believe it has to do with passing a reference to an object that is modified inside
the method.

It is as if the object inside the method was starting to be
destroyed while the calling statement is trying to update its variable.

(in fact, I sometimes get a rectangle where all 4
coordinates are wrong).

At first I thought it was a peculiarity of Python. But
I build a separate test app without the wx.rect, where I create a simple
rectangle class from a list with four elements, and ran a similar test. I
had no problem passing a pointer to a list that was modified inside the class
method out to a calling statement.

Please help me understand this. I don’t mind returning
copies of objects modified in a method– I’d just like to know why I
have to do this.

Norm Frenette

Normand Frenette wrote:

Please help me understand this. I don’t mind returning copies of objects modified in a method– I’d just like to know why I have to do this.

There is a bug in the wrapper for the Inflate method. It is currently returning a reference to the same C++ object instead of making a copy, and so that particular wx.Rect will end up being referenced by two Python objects, and one of them 'owns' it, so it can easily be clobbered when the one destroys it and the other still exists. Actually, I'm a little surprised this hasn't turned up as a more serious crashing problem by now, but I guess that wxRect is simple enough that that could be more rare...

For now I think it would be safe to ignore the return value of Inflate and just return r0 from your translate function, since Inflate will modify the object it is called on.

···

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