wx.EVT_PAINT events: does sizer-type matter?

For more hours than I'd like to admit I've trying to put a custom
control (with a wx.EVT_PAINT handler) in a sizer.
I eventually managed a very simple example, but was surprised that the
only difference between the example that worked and the many that
didn't was the type of sizer in which I placed the control:
wx.gridsizer = working, BoxSizers/Flexgridsizers=not working (see
code below). On further investigation it seems that when using
BoxSizers or Flexgridsizers an EVT_PAINT event is never raised, since
the bound event handler never runs. Can anyone explain what's going
on?

import wx

class Canvas(wx.PyControl):
  def __init__(self, parent):
    super(Canvas, self).__init__(parent)

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

  def OnPaint(self, event):
    print "OnPaint called as expected"
    dc = wx.PaintDC(self)
    dc.DrawText("Hello World!", 5, 5)

class MyApp(wx.App):
  def OnInit(self):
    self.frame = Example(None, title="Top frame")
    self.frame.Show()
    return True

class Example(wx.Frame):
  def __init__(self, parent, title):
    super(Example, self).__init__(parent, title=title)

    self.panel = MyPanel(self)

    frameSizer = wx.BoxSizer(wx.VERTICAL)
    frameSizer.Add(self.panel, 1, wx.EXPAND)
    self.SetSizer(frameSizer)

class MyPanel(wx.Panel):

  def __init__(self, parent):
    super(MyPanel, self).__init__(parent)

    self.__DoLayout()

  def __DoLayout(self):

    wdg = Canvas(self)
    # if msizer is a BoxSizer nothing is visible, and OnPaint is never
called.
    # if msizer is a GridSizer it works as expected

    #msizer = wx.BoxSizer(wx.VERTICAL)
    msizer = wx.GridSizer(1,1,0,0)

    msizer.AddSpacer((20,20))
    msizer.Add(wdg, 0, wx.EXPAND, 5)
    msizer.AddSpacer((20,20))

    self.SetSizer(msizer)

if __name__ == '__main__':

  app = MyApp(False)
  app.MainLoop()

Hi,

For more hours than I'd like to admit I've trying to put a custom
control (with a wx.EVT_PAINT handler) in a sizer.
I eventually managed a very simple example, but was surprised that the
only difference between the example that worked and the many that
didn't was the type of sizer in which I placed the control:
wx.gridsizer = working, BoxSizers/Flexgridsizers=not working (see
code below). On further investigation it seems that when using
BoxSizers or Flexgridsizers an EVT_PAINT event is never raised, since
the bound event handler never runs. Can anyone explain what's going
on?

import wx

class Canvas(wx.PyControl):
def __init__(self, parent):
super(Canvas, self).__init__(parent)

           self\.Bind\(wx\.EVT\_PAINT, self\.OnPaint\)

   def OnPaint\(self, event\):
           print "OnPaint called as expected"
           dc = wx\.PaintDC\(self\)
           dc\.DrawText\("Hello World\!", 5, 5\)

You are subclassing PyControl which doesn't have any sizing logic
built in. At a minimum you should override its DoGetBestSize method to
return the best size of the control. Either that or you can derive
from Panel which can handle the sizing for you if you don't require
custom size management. Only really need to derive from the Py
prefixed classes if you need to override the virtual methods exported
by those versions of the classes.

EVT_PAINT is probably not getting sent because the control is being
initialized to a zero size or something.

The GridSizer is probably applying some minimal size to the control
based on the fixed minimum cell sizes of the 'grid'.

Cody

···

On Wed, Feb 1, 2012 at 10:09 AM, Paul <pazerp@gmail.com> wrote:

Yep, a wx.GridSizer will set all cells to the same size, and a the size of the cells in a wx.FlexGridSizer will be influenced by other items in the same row and col.

Paul, you should take a look at the WIT: http://wiki.wxpython.org/Widget_Inspection_Tool Adding it to your applications (perhaps optionally) will make diagnosing layout issues lots easier.

···

On 2/1/12 8:22 AM, Cody wrote:

Hi,

On Wed, Feb 1, 2012 at 10:09 AM, Paul<pazerp@gmail.com> wrote:

For more hours than I'd like to admit I've trying to put a custom
control (with a wx.EVT_PAINT handler) in a sizer.
I eventually managed a very simple example, but was surprised that the
only difference between the example that worked and the many that
didn't was the type of sizer in which I placed the control:
wx.gridsizer = working, BoxSizers/Flexgridsizers=not working (see
code below). On further investigation it seems that when using
BoxSizers or Flexgridsizers an EVT_PAINT event is never raised, since
the bound event handler never runs. Can anyone explain what's going
on?

import wx

class Canvas(wx.PyControl):
        def __init__(self, parent):
                super(Canvas, self).__init__(parent)

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

        def OnPaint(self, event):
                print "OnPaint called as expected"
                dc = wx.PaintDC(self)
                dc.DrawText("Hello World!", 5, 5)

You are subclassing PyControl which doesn't have any sizing logic
built in. At a minimum you should override its DoGetBestSize method to
return the best size of the control. Either that or you can derive
from Panel which can handle the sizing for you if you don't require
custom size management. Only really need to derive from the Py
prefixed classes if you need to override the virtual methods exported
by those versions of the classes.

EVT_PAINT is probably not getting sent because the control is being
initialized to a zero size or something.

The GridSizer is probably applying some minimal size to the control
based on the fixed minimum cell sizes of the 'grid'.

--
Robin Dunn
Software Craftsman

"At a minimum you should override its DoGetBestSize method to
return the best size of the control"

Thanks Cody. I thought it must have something to do with this, since
many of the examples
in your book use it. Simply returning an appropriate wx.Size() has
sorted it.

"Paul, you should take a look at the WIT"
I will do. I'd never heard of it before now, but it looks handy!