OGL crash with LineShape labels

Hi,

I am getting a crash (IndexError) with wxPython 2.5.4.1 on MSW, Python 2.3, if I use a wxLine with an associated label and I click on it, invoking OnLeftClick. See attached sample, which is basically a stripped down version of the OGL demo, but with a clickable ogl.LineShape _with a label_. The traceback is:

Traceback (most recent call last):
   File "C:\Python23\Lib\site-packages\wx-2.5.4-msw-unicode\wx\lib\ogl\_canvas.py", line 219, in OnMouseEvent
     nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
   File "C:\Documents and Settings\davides\My Documents\Software\Packages\eclipse-SDK-3.0.1-win32\eclipse\workspace\wxPython Stuff\broken_line.py", line 24, in OnLeftClick
     shape.Select(True, dc)
   File "C:\Python23\lib\site-packages\wx-2.5.4-msw-unicode\wx\lib\ogl\_lines.py", line 1114, in Select
     if self._regions[i]:
IndexError: list index out of range

If you comment out the line

line.AddText("Line Label")

from the sample below, or if you don't click on the label, no crash. I had a quick look at the OGL source, and it seems to me that the IndexError is generated because OnLabelMovePre() (file _lines.py) removes one of the LineShape regions. (I don't follow the logic being used there)

I tried to run the same code on a system with wxPython 2.4.2.4 and the old C++ OGL, and I am getting a crash there as well (wxPyDeadObjectError: The C++ part of the wxPyRectangleShapePtr object has been deleted, attribute access no longer allowed), although to make it crash there you first need to click on the line, and then on one of the nodes.

Regards,
Davide

···

====
import wx
import wx.lib.ogl as ogl

class MyEvtHandler(ogl.ShapeEvtHandler):
     def __init__(self, frame):
         ogl.ShapeEvtHandler.__init__(self)
     def OnLeftClick(self, x,y, keys=0, attachment=0):
         shape = self.GetShape()
         canvas = shape.GetCanvas()
         dc = wx.ClientDC(canvas)
         canvas.PrepareDC(dc)

         if shape.Selected():
             shape.Select(False, dc)
             canvas.Redraw(dc)
         else:
             shapeList = canvas.GetDiagram().GetShapeList()
             toUnselect = []

             for s in shapeList:
                 if s.Selected():
                     toUnselect.append(s)
             shape.Select(True, dc)

             if toUnselect:
                 for s in toUnselect:
                     s.Select(False, dc)
                 canvas.Redraw(dc)

class TestWindow(ogl.ShapeCanvas):
     def __init__(self, parent, frame):
         ogl.ShapeCanvas.__init__(self, parent)
         self.frame = frame
         self.diagram = ogl.Diagram()
         self.SetDiagram(self.diagram)
         self.diagram.SetCanvas(self)
         self.shapes = []

         # a couple of shapes
         node1 = self.MyAddShape(ogl.CircleShape(80),
             75, 110, wx.Pen(wx.BLUE, 3), wx.GREEN_BRUSH, "Node 1")
         node2 = self.MyAddShape(ogl.CircleShape(80),
             350, 110, wx.Pen(wx.BLUE, 3), wx.GREEN_BRUSH, "Node 2")

         dc = wx.ClientDC(self)
         self.PrepareDC(dc)

         # now link them with a LineShape
         line = ogl.LineShape()
         line.SetCanvas(self)
         line.MakeLineControlPoints(2)
         node1.AddLine(line, node2)

         if 1:
             # add a label: this crashes with IndexError
             line.AddText("Line Label")

         self.diagram.AddShape(line)
         node1.Move(dc, 75, 110) # it seems required w/ wx 2.4 at least to display the line
         line.Show(True)

         eh = MyEvtHandler(self.frame)
         eh.SetShape(line)
         eh.SetPreviousHandler(line.GetEventHandler())
         line.SetEventHandler(eh)

     def MyAddShape(self, shape, x, y, pen, brush, text):
         shape.SetCanvas(self)
         if x: shape.SetX(x)
         if y: shape.SetY(y)
         if pen: shape.SetPen(pen)
         if brush: shape.SetBrush(brush)
         if text: shape.AddText(text)
         self.diagram.AddShape(shape)
         shape.Show(True)

         eh = MyEvtHandler(self.frame)
         eh.SetShape(shape)
         eh.SetPreviousHandler(shape.GetEventHandler())
         shape.SetEventHandler(eh)

         self.shapes.append(shape)
         return shape

class CrashDemo(wx.App):
     def __init__(self):
         wx.App.__init__(self, redirect=False)

     def OnInit(self):
         frame = wx.Frame(None, -1, "wx Test", pos=(50,50), size=(200,100),
                          style=wx.DEFAULT_FRAME_STYLE)
         frame.Show(True)
         ogl.OGLInitialize()
         win = TestWindow(frame, frame)
         frame.SetSize((640,480))
         win.SetFocus()
         self.frame = frame
         return True

     def OnExitApp(self, evt):
         self.frame.Close(True)

if __name__ == "__main__":
     app = CrashDemo()
     app.MainLoop()

Davide Salomoni wrote:

If you comment out the line

line.AddText("Line Label")

from the sample below, or if you don't click on the label, no crash. I had a quick look at the OGL source, and it seems to me that the IndexError is generated because OnLabelMovePre() (file _lines.py) removes one of the LineShape regions. (I don't follow the logic being used there)

I don't understand it either. We'll probably have to wait for Pierre to take a look at this.

···

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

Robin Dunn <robin@alldunn.com> writes:

> Davide Salomoni wrote:
>
> >
> > If you comment out the line
> >
> > line.AddText("Line Label")
> >
> > from the sample below, or if you don't click on the label, no crash. I
> > had a quick look at the OGL source, and it seems to me that the
> > IndexError is generated because OnLabelMovePre() (file _lines.py)
> > removes one of the LineShape regions. (I don't follow the logic being
> > used there)
> >
>
> I don't understand it either. We'll probably have to wait for Pierre to
> take a look at this.
>
This one was so stupid I can't believe I wrote the code...

Here's a quick diff on _lines.py (it's from 2.5.3.1, so I hope
the line numbers are correct). Hopefully it won't break something else.
The demo seems to work ok though.

1517c1517,1522
< i = self._regions.index(labelShape._shapeRegion)

···

---

        i = 0
        for region in self.GetRegions():
            if labelShape._shapeRegion == region:
                self.GetRegions().remove(region)
            else:
                i += 1

--
  Pierre Hjälm