wx.Filesystem/wx.File crashes wxPython 2.5.2.7

I make use of wx.Filesystem and wx.File in my code to allow
images to be loaded into an HTML window. In wxPython 2.4.2.4
all works well. In wxPython 2.5.2.7, the whole process gets
wiped out on both Linux and Windows. My debugging so far seems to indicate that the underlying C objects are being freed and then used.

Fortunately I was able to make a short sample (attached).

Invoke it giving the name of a directory. All the image
files in that directory will be displayed in an HTML
Window (2.4) or the process dies (2.5).

Roger

wxfile.py (1.39 KB)

Roger Binns wrote:

I make use of wx.Filesystem and wx.File in my code to allow
images to be loaded into an HTML window. In wxPython 2.4.2.4
all works well. In wxPython 2.5.2.7, the whole process gets
wiped out on both Linux and Windows. My debugging so far seems to indicate that the underlying C objects are being freed and then used.

Fortunately I was able to make a short sample (attached).

Invoke it giving the name of a directory. All the image
files in that directory will be displayed in an HTML
Window (2.4) or the process dies (2.5).

There is a bug in the reference counting and so the C++ object is deleted too soon. It's easy to fix for the next build, but unfortunatly the only work around I could think of simply delayed the crash until later. :frowning:

···

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

Robin Dunn wrote:

There is a bug in the reference counting and so the C++ object is
deleted too soon. It's easy to fix for the next build, but
unfortunatly the only work around I could think of simply delayed the
crash until later. :frowning:

Is there any chance of 2.5.2.8 in the very near future? This is
unfortunately a show stopper for me moving to 2.5.

It may be worth adding a "private" method such as _add_ref
to all Swig defined classes to provide work arounds in
the future, since this kind of thing has happened before.

If I write a CPython module that just does a Py_INCREF
would that be a suitable fix?

Roger

Hi All,

I'm trying to extend the wxOGL demo for wxPython to use also composite
blocks,
but I failed in producing a composite shape able to be dragged with the
mouse like all
the other shapes can.

I don't believe it is a bug but more probably there is something of
undocumented about the event
management of composite shapes that I should implement but I didn't.

Appended below there is a sample code derived from the OGL demo to
illustrate the problem.
It shows a canvas with 2 shapes,
the rectangular one can be dragged with the mouse without have to define any
event handler
while the composite one is "mouse-unsensitive" at all.

I ran it under python 2.3, wxPython 2.5.1.5u, Mandrake Linux 9.2

Cheers
Massimo Bertani

-----cut here----------------cut here-----------------cut
here------------------

#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-

import wx # This module uses the new wx namespace
import sys, os
import wx.ogl as ogl

# stuff for debugging
print "wx.VERSION_STRING = ", wx.VERSION_STRING
print "pid:", os.getpid()
##raw_input("Press a key...")

assertMode = wx.PYAPP_ASSERT_DIALOG
##assertMode = wx.PYAPP_ASSERT_EXCEPTION

class GenericCompoShape(ogl.CompositeShape):
    def __init__(self, canvas, x=100, y=100):
        ogl.CompositeShape.__init__(self)

        self.w = 40
        self.h = 80
        self.x = x
        self.y = y

        self.rect = ogl.RectangleShape(self.w, self.h)
        self.rect.SetX(self.x)
        self.rect.SetY(self.y)
        self.rect.SetBrush(wx.WHITE_BRUSH)
        self.rect.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID))
        self.rect.AddText("compo")
        self.AddChild(self.rect)

        for i in range(5):
            pin = ogl.RectangleShape(8.0, 8.0)
            pin.SetY(self.y + ((-self.h/2.0 + 10 * (i + 1) + 1)))
            pin.SetPen(wx.Pen("FOREST GREEN", 1, wx.SOLID))
            pin.SetBrush(wx.Brush("FOREST GREEN"))
            self.AddChild(pin)
            self.AddSimpleConstraint(ogl.gyCONSTRAINT_MIDALIGNED_LEFT,
self.rect, pin)

        self.SetX(self.x)
        self.SetY(self.y)
        self.SetDraggable(True, True)
        canvas.diagram.AddShape(self)
        self.Show(True)

        print "constraints ok:",self.Recompute()

···

#------------------------------------------------------------
class GenericRectShape(ogl.RectangleShape):
    def __init__(self, canvas, x=100, y=100):
        ogl.RectangleShape.__init__(self,30,60)

        self.SetX(x)
        self.SetY(y)
        self.canvas = canvas
        self.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID))
        self.SetBrush(wx.WHITE_BRUSH)

        self.AddText("rect")
        self.canvas.diagram.AddShape(self)
        self.Show(True)
#----------------------------------------------------------------------

class TestWindow(ogl.ShapeCanvas):
    def __init__(self, parent, log, frame):
        ogl.ShapeCanvas.__init__(self, parent)

        maxWidth = 1000
        maxHeight = 1000
        self.SetScrollbars(20, 20, maxWidth/20, maxHeight/20)

        self.log = log
        self.frame = frame
        self.SetBackgroundColour("LIGHT BLUE") #wx.WHITE)
        self.diagram = ogl.Diagram()
        self.SetDiagram(self.diagram)
        self.diagram.SetCanvas(self)
        self.shapes = []
        #self.save_gdi = []

        #rRectBrush = wx.Brush("MEDIUM TURQUOISE", wx.SOLID)
        #dsBrush = wx.Brush("WHEAT", wx.SOLID)

        self.shapes.append(GenericCompoShape(self,300,120))
        self.shapes.append(GenericRectShape(self,200,140))

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

        for x in range(len(self.shapes)):
            fromShape = self.shapes[x]

            if x+1 == len(self.shapes):
                toShape = self.shapes[0]
            else:
                toShape = self.shapes[x+1]

            line = ogl.LineShape()
            line.SetCanvas(self)
            line.SetPen(wx.BLACK_PEN)
            line.SetBrush(wx.BLACK_BRUSH)
            line.AddArrow(ogl.ARROW_ARROW)
            line.MakeLineControlPoints(2)
            fromShape.AddLine(line, toShape)
            self.diagram.AddShape(line)
            line.Show(True)

            # for some reason, the shapes have to be moved for the line to
show up...
            fromShape.Move(dc, fromShape.GetX(), fromShape.GetY())

        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)

    def OnDestroy(self, evt):
        # Do some cleanup
        for shape in self.diagram.GetShapeList():
            if shape.GetParent() == None:
                shape.SetCanvas(None)
                shape.Destroy()

        self.diagram.Destroy()

    def OnBeginDragLeft(self, x, y, keys):
        self.log.write("OnBeginDragLeft: %s, %s, %s\n" % (x, y, keys))

    def OnEndDragLeft(self, x, y, keys):
        self.log.write("OnEndDragLeft: %s, %s, %s\n" % (x, y, keys))

    def OnRightClick(self, *dontcare):
        print "OnRightClick"

    def OnLeftClick(self, *dontcare):
        print "OnLeftClick"
#----------------------------------------------------------------------

# The OGL library holds some resources that need to be freed before
# the app shuts down.
class __Cleanup:
    def __del__(self, cleanup=ogl.OGLCleanUp):
        cleanup()

# When this module gets cleaned up by Python then __cu will be cleaned
# up and it's __dell__ is called, which will then call ogl.OGLCleanUp.
__cu = __Cleanup()

#---------------------------------------------------------------------------
-

class Log:
    def WriteText(self, text):
        if text[-1:] == '\n':
            text = text[:-1]
        wx.LogMessage(text)
    write = WriteText

class RunDemoApp(wx.App):
    def __init__(self, useShell):
        self.useShell = useShell
        wx.App.__init__(self, 0)

    def runTest(self, frame, nb, log):
        # This creates some pens and brushes that the OGL library uses.
        # It should be called after the app object has been created, but
        # before OGL is used.
        ogl.OGLInitialize()

        win = TestWindow(nb, log, frame)
        return win

    def OnInit(self):
        wx.InitAllImageHandlers()
        wx.Log_SetActiveTarget(wx.LogStderr())

        self.SetAssertMode(assertMode)

        frame = wx.Frame(None, -1, "Programma di prova OGL", pos=(50,50),
size=(200,100),
                        style=wx.NO_FULL_REPAINT_ON_RESIZE|wx.DEFAULT_FRAME_
STYLE)
        frame.CreateStatusBar()

        menuBar = wx.MenuBar()
        menu = wx.Menu()
        item = menu.Append(-1, "E&xit\tAlt-X", "Exit demo")
        self.Bind(wx.EVT_MENU, self.OnButton, item)
        menuBar.Append(menu, "&File")

        ns = {}
        ns['wx'] = wx
        ns['app'] = self
        ns['frame'] = frame

        frame.SetMenuBar(menuBar)
        frame.Show(True)
        frame.Bind(wx.EVT_CLOSE, self.OnCloseFrame)

        win = self.runTest(frame, frame, Log())

        # so set the frame to a good size for showing stuff
        frame.SetSize((640, 480))
        win.SetFocus()
        self.window = win
        ns['win'] = win
        frect = frame.GetRect()

        self.SetTopWindow(frame)
        self.frame = frame
        #wx.Log_SetActiveTarget(wx.LogStderr())
        #wx.Log_SetTraceMask(wx.TraceMessages)

        if self.useShell:
            # Make a PyShell window, and position it below our test window
            from wx import py
            shell = py.shell.ShellFrame(None, locals=ns)
            frect.OffsetXY(0, frect.height)
            frect.height = 400
            shell.SetRect(frect)
            shell.Show()

            # Hook the close event of the test window so that we close
            # the shell at the same time
            def CloseShell(evt):
                if shell:
                    shell.Close()
                evt.Skip()
            frame.Bind(wx.EVT_CLOSE, CloseShell)

        return True

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

    def OnCloseFrame(self, evt):
        if hasattr(self, "window") and hasattr(self.window, "ShutdownDemo"):
            self.window.ShutdownDemo()
        evt.Skip()

#---------------------------------------------------------------------------
-

def main(argv):
    useShell = False
    for x in range(len(sys.argv)):
        if sys.argv[x] in ['--shell', '-shell', '-s']:
            useShell = True
            del sys.argv[x]
            break
    app = RunDemoApp(useShell)
    app.MainLoop()

if __name__ == "__main__":
    main(sys.argv)

"giuseppe massimo bertani" <gm_bertani@yahoo.it> writes:

>
> Hi All,
>
> I'm trying to extend the wxOGL demo for wxPython to use also composite
> blocks,
> but I failed in producing a composite shape able to be dragged with the
> mouse like all
> the other shapes can.
...
> I ran it under python 2.3, wxPython 2.5.1.5u, Mandrake Linux 9.2
>
I was very confused about all this till I saw the above line.
wxPython 2.5.2.x has a new OGL written in python, and the demo has
been expanded to include CompositeShape (and more). Check it out.

···

--
  Pierre Hjälm

Roger Binns wrote:

Robin Dunn wrote:

There is a bug in the reference counting and so the C++ object is
deleted too soon. It's easy to fix for the next build, but
unfortunatly the only work around I could think of simply delayed the
crash until later. :frowning:

Is there any chance of 2.5.2.8 in the very near future?

Yes. I was considering it anyway because of some of the little bugs that have been fixed, but this one cliches it. Watch for a test build in the next day or so.

This is
unfortunately a show stopper for me moving to 2.5.

It may be worth adding a "private" method such as _add_ref
to all Swig defined classes to provide work arounds in
the future, since this kind of thing has happened before.

In this case it would not have helped. At best it would lead to a memory leak, but more likely (as with the workaround I mentioned before) it would only delay the crash until the extra ref was finally decrefed and it attempts to destroy the C++ object second time.

If I write a CPython module that just does a Py_INCREF
would that be a suitable fix?

This isn't any different than the above in net effect.

After a flash of insite while in the shower, here is another workaround that is similar to (and compatible with) what will be done in the new version of the bugfix:

     def OpenFile(self, filesystem, location):
         print "handling: ", location
         r = self.GetRightLocation(location)
         f = MyImageFile(os.path.join(self.imgdir, r), location)
         f.thisown = 0 # <-- this is the important line
         return f

···

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

Robin Dunn wrote:

After a flash of insite while in the shower, here is another
workaround

Thanks - that worked perfectly. (Some of my best thinking
is in the shower as well :slight_smile:

I'd also like to take the opportunity of reminding you of this
email, from just over a year ago:

http://lists.wxwidgets.org/archive/wxPython-users/msg21493.html

I am busy fighting an issue with HtmlHelpController on Windows
and would be more than happy to move to CHMHelpController.

Roger