Drawing multiple custom controls directly onto a panel

I’ve created a basic custom widget using wxPyControl, and now want to create numerous instances of this control and place them onto a
a panel using my own sizing logic (i.e. without using any sizers). What is the best way to do this? As it stands my controls and my panel each have an OnPaint method that responds to a resize event (see below), and whilst these methods are sufficient to display the controls when placed into a sizer, they are insufficient if I don’t use one - in fact, nothing at all appears on screen. I’m not surprised by this, but I can’t work out what I need to do to draw each control directly onto the panel. Do I need a separate draw method in my control class which is called by the panel’s OnPaint method, and if so, what kind of device context am I drawing on to? Attached screenshot shows roughly what I’m working toward; each word in this screenshot is represented by one of my custom controls, hence there will be upwards of thirty on screen at any one time.

class MyCustomControl

def OnPaint(self, event):
    """This will be drawn onto the window"""
    dc = wx.PaintDC(self)

    font = dc.GetFont()
    font.SetPointSize(self.sze)
    font.SetUnderlined(self.isUnderlined)
    dc.SetFont(font)
    dc.GetTextExtent(self.word)
    dc.SetTextForeground(self.col)
    rect = self.GetClientRect()
    dc.DrawLabel(self.word, rect, wx.ALIGN_BOTTOM)

screenshot.png

I've created a basic custom widget using wxPyControl, and now want to create
numerous instances of this control and place them onto a
a panel using my own sizing logic (i.e. without using any sizers). What is
the best way to do this?

Simply put them on the panel, setting the size and pos arguments yourself.

You can change them later with the .Position and .Size properties.

as it stands my controls and my panel each have an
OnPaint method that responds to a resize event (see below),

IT's not responding ot a resize event, it's responding to a Paint
Event (or it should be, anyway) the resize is triggering the Paint
Event.

Simply putting the control on a Panel should trigger Paint events at
the right times.

IF this does work, post a full sample:

http://wiki.wxpython.org/MakingSampleApps

But you really should be using sizers, anyway...

-Chris

···

On Thu, Sep 20, 2012 at 7:44 AM, Paul <pazerp@gmail.com> wrote:

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

There are two approaches that would work here, and each have advantages and disadvantages.

1. The words are a custom widget. As Chris mentioned you can use the position and size attributes to lay out the words however you want. However instead of triggering it from the parent's EVT_PAINT event I would do it instead from the parent's EVT_SIZE event handler.

The approach has the advantage of simpler drawing since each word widget will paint itself, mouse event hit-testing if you need it will be easier since the word widgets can handle that themselves, and layout is a bit easier too. Disadvantages are that there are many individual paint events and in addition to the extra overhead of all the contols and events, on Windows it's possible that you could see some flicker from this.

2. The words are a non-widget class. In this case there would be only one actual widget, the panel, and it would draw all the words itself in its EVT_PAINT handler. It could have a collection of word objects that carry any other information about them that you need, and they could even have something like a draw(self, dc, rect) method so they will still be responsible to paint themselves on the panel's DC.

Advantages are that there will be only one paint event, it would be easy to double buffer if needed, if you need to allow overlapping words that will work out much better without separate widgets, and there is not any other per-widget overhead to deal with. The disadvantages are that the paint handler and mouse hit-testing will be a bit more complex (but not terribly so).

···

On 9/20/12 7:44 AM, Paul wrote:

I've created a basic custom widget using wxPyControl, and now want to
create numerous instances of this control and place them onto a
a panel using my own sizing logic (i.e. without using any sizers). What
is the best way to do this? As it stands my controls and my panel each
have an OnPaint method that responds to a resize event (see below), and
whilst these methods are sufficient to display the controls when placed
into a sizer, they are insufficient if I don't use one - in fact,
nothing at all appears on screen. I'm not surprised by this, but I can't
work out what I need to do to draw each control directly onto the panel.
Do I need a separate draw method in my control class which is called by
the panel's OnPaint method, and if so, what kind of device context am I
drawing on to? Attached screenshot shows roughly what I'm working
toward; each word in this screenshot is represented by one of my custom
controls, hence there will be upwards of thirty on screen at any one time.

--
Robin Dunn
Software Craftsman