wxpython: adding panel to wx.Frame disables/conflicts with wx.Frame's OnPaint?

1.7k views Asked by At

I just encountered this strange situation: I found an example, where wx.Frame's OnPaint is overridden, and a circle is drawn. Funnily, as soon as I add even a single panel to the frame, the circle is not drawn anymore - in fact, OnPaint is not called at all anymore ! (Btw, I tried the example out on Ubuntu Lucid)

Can anyone explain me if this is the expected behavior, and how to correctly handle a wx.Frame's OnPaint, if the wx.Frame has child panels ? Small code example is below..

Thanks in advance for any answers,
Cheers!

The code:

#!/usr/bin/env python

# http://www.linuxquestions.org/questions/programming-9/wxwidgets-wxpython-drawing-problems-with-onpaint-event-703946/

import wx

class MainWindow(wx.Frame):
    def __init__(self, parent, title, size=wx.DefaultSize):
        wx.Frame.__init__(self, parent, wx.ID_ANY, title, wx.DefaultPosition, size)

        self.circles = list()
        self.displaceX = 30
        self.displaceY = 30

        circlePos = (self.displaceX, self.displaceY)
        self.circles.append(circlePos)

        ## uncommenting either only first, or both of 
        ## the commands below, causes OnPaint *not* to be called anymore! 
        #~ self.panel = wx.Panel(self, wx.ID_ANY)
        #~ self.mpanelA = wx.Panel(self.panel, -1, size=(200,50))

        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, e):
        print "OnPaint called"
        dc = wx.PaintDC(self)
        dc.SetPen(wx.Pen(wx.BLUE))
        dc.SetBrush(wx.Brush(wx.BLUE))

        # Go through the list of circles to draw all of them
        for circle in self.circles:
            dc.DrawCircle(circle[0], circle[1], 10)


def main():
    app = wx.App()
    win = MainWindow(None, "Draw delayed circles", size=(620,460))
    win.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()
1

There are 1 answers

0
sdaau On BEST ANSWER

OK, this isn't really trivial, I guess... But I found the thread "wxPython - drawing without paint event - Python answers", where it is mentioned:

One nice strategy from an example in "wxPython in Action" is the following:

  • the frame has Draw methods that draw into a BufferedDC, which is chained to a bitmap member,
  • inside the paint method, the bitmap member is drawn to screen

... however, that is actually slightly misleading - if we look at one of those examples, such as Chapter-06/example1.py, it is noticeable that the app spawns a wx.Frame (as in my example); but the wx.Frame here is simply initialized by instantiating a wx.Window - and it is here where all this DC onPaint stuff happens.

With that in mind, my code above can be modified, so it finally works again (i.e. blue circle rendered), as below:

#!/usr/bin/env python

# http://www.linuxquestions.org/questions/programming-9/wxwidgets-wxpython-drawing-problems-with-onpaint-event-703946/

import wx

class MainWindowWindow(wx.Window):
    def __init__(self, parent):
        wx.Window.__init__(self, parent)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.circles = list()
        self.displaceX = 30
        self.displaceY = 30

        circlePos = (self.displaceX, self.displaceY)
        self.circles.append(circlePos)

        ## uncommenting either only first, or both of 
        ## the commands below, now calls onPaint
        ## (without these panels, OnPaint called once - with them, twice)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.mpanelA = wx.Panel(self.panel, -1, size=(200,50))

    def OnPaint(self, e):
        print "OnPaint called"
        dc = wx.PaintDC(self)
        dc.SetPen(wx.Pen(wx.BLUE))
        dc.SetBrush(wx.Brush(wx.BLUE))

        # Go through the list of circles to draw all of them
        for circle in self.circles:
            dc.DrawCircle(circle[0], circle[1], 10)


class MainWindow(wx.Frame):
    def __init__(self, parent, title, size=wx.DefaultSize):
        wx.Frame.__init__(self, parent, wx.ID_ANY, title, wx.DefaultPosition, size)
        MainWindowWindow(self)


def main():
    app = wx.App()
    win = MainWindow(None, "Draw delayed circles", size=(620,460))
    win.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

Well, hope this helps someone,
Cheers!