wxPy25 - slowness and refreshing dc

Hi Robin Dunn and all wxPy2.5 testers

I was complaining about the slowness of wxPy2.5,
slowness in Refreshing/Repainting.

This afternoon, I worked on some drawing applications
and I found interesting points.

1) In a first application, see listing below,
I noticed, that it was impossible to refresh
my drawing correctly (OnPaint event). This app
uses a buffered bitmap. After a resize, which
forces a repainting, the image is not refreshed
correctly. It is only partly cleared, before a fresh
redraw, leading on the superpostion of previous
"broken" images.

2) I suspected an error in my app, so I tried it
with wxPython 2.4.2.4 -import wx style-.
It was working fine.

3) I had the idea to put some "clock()" functions
in my app and I compared wxPy 2.4.2.4 with the
actual wxPy 2.5. I measured the time to blit
the buffered bitmap. The result is surprising,
the ratio wxPy2424 : wxPy2.5, is ~1:4.

I'm aware, I have not the latest pc model,
but this difference is not negligeable.

Ideas, comments, where is the bottleneck ?

Jean-Michel Fauth, Switzerland

#-*- coding: iso-8859-1 -*-

···

#=========================================================================
# testgraphrobin.py
# python 2.3, win98se, wxPython 2.4.2.4 / 2.5 beta
# Jean-Michel Fauth
# 30 Novembre 2003
#=========================================================================

import wx
from time import asctime, clock

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

def jmtime():
    return '[' + asctime()[11:19] + '] '

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

class MyDrawingArea(wx.Window):
    
    def __init__(self, parent, id):
        sty = wx.NO_BORDER
        wx.Window.__init__(self, parent, id, style=sty)
        self.parent = parent
        self.SetBackgroundColour(wx.WHITE)
        self.SetCursor(wx.CROSS_CURSOR)

        self.BufferBmp = None
        self.BufferBmpWidth = None
        self.BufferBmpHeight = None
        self.memdc = None
        
        #wxPy 25 only
        #~ self.Bind(wx.EVT_SIZE, self.OnSize)
        #~ self.Bind(wx.EVT_PAINT, self.OnPaint)
        
        #wxPy 2424 and 25
        wx.EVT_SIZE(self, self.OnSize)
        wx.EVT_PAINT(self, self.OnPaint)

    def OnSize(self, event):
        print jmtime() + 'OnSize'
        self.BufferBmpWidth, self.BufferBmpHeight = self.GetSizeTuple()
        self.BufferBmp = wx.EmptyBitmap(self.BufferBmpWidth, self.BufferBmpHeight)
        self.memdc = wx.MemoryDC()
        self.memdc.SelectObject(self.BufferBmp)
        self.DoSomeDrawing(self.memdc)
        
    def OnPaint(self, event):
        print jmtime() + 'OnPaint'
        dc = wx.PaintDC(self)
        dc.BeginDrawing()
        if self.BufferBmp is not None:
            
            t1 = clock()
            #wxPy 2424
            #~ dc.Blit(0, 0, self.BufferBmpWidth, self.BufferBmpHeight, self.memdc, 0, 0)
            #wxPy 25
            dc.BlitXY(0, 0, self.BufferBmpWidth, self.BufferBmpHeight, self.memdc, 0, 0)
            t2 = clock()
            print 'dt =', (t2 - t1) * 10000.0
            #-------------------------------------------------
            #the ratio wxPy 2.4.2.4 : wxPy 2.5 beta is 1 : 4 !
            #-------------------------------------------------
            
        dc.EndDrawing()
    
    def DoSomeDrawing(self, dc):
        dc.BeginDrawing()
        
        gap = 40
        le, to = gap, gap
        wi, he = dc.GetSizeTuple()
        wi, he = wi - 2 * gap, he - 2 * gap
        
        dc.SetBrush(wx.Brush(wx.CYAN, wx.SOLID))
        dc.Clear()

        dc.SetPen(wx.Pen(wx.RED, 1, wx.SOLID))
        
        #wxPy 2424
        #~ dc.DrawRectangle(le, to, wi, he)
        #wxPy 25
        dc.DrawRectangleXY(le, to, wi, he)
            
        dc.EndDrawing()
        
#-------------------------------------------------------------------

class MyPanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize)
        
        self.drawingarea = MyDrawingArea(self, -1)

        self.SetAutoLayout(True)
        gap = 10
        lc = wx.LayoutConstraints()
        lc.top.SameAs(self, wx.Top, gap)
        lc.left.SameAs(self, wx.Left, gap)
        lc.right.SameAs(self, wx.Width, gap)
        lc.bottom.SameAs(self, wx.Bottom, gap)
        self.drawingarea.SetConstraints(lc)

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

class MyFrame(wx.Frame):

    def __init__(self, parent, id):
        s = __file__
        wx.Frame.__init__(self, parent, id, s, wx.Point(0, 0), wx.Size(500, 400))
        self.panel = MyPanel(self, -1)

        #~ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        wx.EVT_CLOSE(self, self.OnCloseWindow)

    def OnCloseWindow(self, event):
        self.Destroy()

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

class MyApp(wx.App):

    def OnInit(self):
        frame = MyFrame(None, -1)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

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

def main():
    print 'main is running...'
    app = MyApp(0)
    app.MainLoop()

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

if __name__ == "__main__" :
    main()

#eof-------------------------------------------------------------------

Jean-Michel Fauth wrote:

Hi Robin Dunn and all wxPy2.5 testers

I was complaining about the slowness of wxPy2.5,
slowness in Refreshing/Repainting.

This afternoon, I worked on some drawing applications
and I found interesting points.

1) In a first application, see listing below,
I noticed, that it was impossible to refresh
my drawing correctly (OnPaint event). This app uses a buffered bitmap. After a resize, which forces a repainting, the image is not refreshed
correctly. It is only partly cleared, before a fresh
redraw, leading on the superpostion of previous
"broken" images.

The wx.NO_FULL_REPAINT_ON_RESIZE style is now the default style for all windows. The name still exists for compatibility, but it is set to zero. If you want to disable the setting (so it matches the old default) then you need to use the new wx.FULL_REPAINT_ON_RESIZE style flag otherwise only the freshly exposed areas of the window will be refreshed.

I'll add a note to the MigrationGuide about this.

2) I suspected an error in my app, so I tried it
with wxPython 2.4.2.4 -import wx style-.
It was working fine.

Yes, that helps confirm the above.

3) I had the idea to put some "clock()" functions
in my app and I compared wxPy 2.4.2.4 with the
actual wxPy 2.5. I measured the time to blit
the buffered bitmap. The result is surprising,
the ratio wxPy2424 : wxPy2.5, is ~1:4.

I'm aware, I have not the latest pc model,
but this difference is not negligeable.

Ideas, comments, where is the bottleneck ?

There were a number of changes to wxBitmap (most of which are not exposed to wxPython yet...) so it is not suprizing that there is a difference, but that is a higher ratio than I would have expected. Perhaps you should bring this up on wx-dev and see if there is any response there from the folks that made the change.

···

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