[wxPython] custom control and resizing

Hello everyone,

I'm quite new to wxPython, so I apologize in advance if this is an FAQ
or in the docs.. I experimented and read for several hours before
resorting to this email :slight_smile:

I'm building a custom control for a chess board. How can I ensure that
the control can be resized, but always remains square?

I can successfully make it always a certain size (although the parent
frame for some reason is allowed to become smaller than this size), and
I can make it resize to the exact same size as the parent frame.

I've tried doing a custom OnSize event handler in the control (it's a
subclass of wxWindow at the moment), but the size events I get are
always the same. And even if I call SetSize in my event handler, the
control stays the same size.

I'm sure I'm misunderstanding how the sizing works :slight_smile: Can someone point
me in the right direction?

jack.

I'm building a custom control for a chess board. How can I ensure that
the control can be resized, but always remains square?

It's possible, but may not be the Right Thing to do. See below.

I can successfully make it always a certain size (although the parent
frame for some reason is allowed to become smaller than this size),

You can call SetSizeHints on the frame to prevent it from shrinking and/or
growing past a certain size.

and
I can make it resize to the exact same size as the parent frame.

I've tried doing a custom OnSize event handler in the control (it's a
subclass of wxWindow at the moment), but the size events I get are
always the same. And even if I call SetSize in my event handler, the
control stays the same size.

If a wxFrame has only one child window then it will ensure that the child
always fills its client area. It is a very common use case that a frame has
only one child and so this behaviour was built in way back in the dawn of
wxWindows time. Unfortunately it usually does get in the way of the newbie.

There are a couple solutions that come to mind. First would be to put a
wxPanel under the chess control as the child of the frame, then it can fill
the frame and the chess board can do whatever it wants. The other thing
(and the one I like) would br to design the chess board such that it doesn't
care if it has a rectangle or not. It can draw the board as a square in the
center of the rectangle and then fill any remaining space with some bitmap
or solid colour...

路路路

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

The other thing
(and the one I like) would br to design the chess board such that it doesn't
care if it has a rectangle or not. It can draw the board as a square in the
center of the rectangle and then fill any remaining space with some bitmap
or solid colour...

Good idea :slight_smile: I'm not sure why I didn't think of that originally.

So I've done that now, and everything is working great, but I have run
into two new problems :slight_smile:

- Resizing doesn't redraw in MS Windows. Or rather, it only redraws the
  part that changed, not the whole window, so it looks really awful.
  Calling self.OnPaint() (my drawing code lives there of course) doesn't
  work either. I'm not sure why this doesn't work there, as it works
  great in linux.

- I've implemented dragging pieces around by handling the LEFT_DOWN,
  LEFT_UP and MOTION mouse events. I assume this is more or less the
  right thing to do, but wouldn't be surprised if I am way off. Then
  for each MOTION event, I call self.OnPaint(). This works on linux,
  but not on windows. I assume the reasons are the same as above.

  Also, performance is slow. I tried making an empty bitmap, drawing
  into that, and then blitting everything at once (which I remember
  being the way to get flicker-free stuff in other toolkits) and this is
  still slow, but not unreasonable. Any way to speed this up?

Thanks much for the help,
jack.

- Resizing doesn't redraw in MS Windows. Or rather, it only redraws the
  part that changed, not the whole window, so it looks really awful.
  Calling self.OnPaint() (my drawing code lives there of course) doesn't
  work either. I'm not sure why this doesn't work there, as it works
  great in linux.

Because you can't use a wxPaintDC except from within an EVT_PAINT event.
See below for an alternative.

- I've implemented dragging pieces around by handling the LEFT_DOWN,
  LEFT_UP and MOTION mouse events. I assume this is more or less the
  right thing to do, but wouldn't be surprised if I am way off. Then
  for each MOTION event, I call self.OnPaint(). This works on linux,
  but not on windows. I assume the reasons are the same as above.

Yes, but you really don't want to repaint the whole window when the only the
area around the bitmap you are dragging needs redrawn. At least you should
set the clipping region so even if you draw the whole thing only the bit
that needs refreshed actually gets put on the screen. Another alternative
is to use wxDragImage, see the demo for an example. Yet another option is
described below.

  Also, performance is slow. I tried making an empty bitmap, drawing
  into that, and then blitting everything at once (which I remember
  being the way to get flicker-free stuff in other toolkits) and this is
  still slow, but not unreasonable. Any way to speed this up?

Are your recreating and redrawing the whole bitmap each time? If so the
first thing to do is to keep the bitmap around and just redraw the parts
that need changing. There is a new class in 2.3.3 to help with this called
wxBufferedDC. You can see an example of using it in the new doodle sample
in CVS:

http://cvs.wxwindows.org/cgi-bin/viewcvs.cgi/*checkout*/wxPython/samples/doo
dle/doodle.py?rev=1.4&content-type=text/plain

Notice the InitBuffer method where the bitmap is created and initialized.
It is called and initialization and also when the window changes size, and
then Refresh is called to repaint the window. (This solves the first
problem above.) OnPaint becomes just a single line of code. When drawing
needs to be done, a new wxBufferedDC is created using the bitmap and drawing
is done to that. When the dc goes out of scope it is automatically blitted
to the screen.

路路路

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

Because you can't use a wxPaintDC except from within an EVT_PAINT event.
See below for an alternative.

Ah. So it's just lucky that it works on linux.

self.Refresh() was what I ended up doing before you had replied.
Unfortunately I do have to redraw the entire bitmap during the OnSize
events. I did a quick survey of similar apps and changing the size of
the window, and now my wxPython code is at least as good as all of them
in terms of refreshing, dragging speed, etc. So I am a happy camper :slight_smile:

that needs refreshed actually gets put on the screen. Another alternative
is to use wxDragImage, see the demo for an example. Yet another option is
described below.

I found wxDragImage and used that. It works quite well.

Thanks much for your help :slight_smile:

jack.

Hi,

When I create a table, I'm finding that when the table sets itself up, GetValue is called for every cell, before the table is even visible.

When the table is running, GetValue does act normally.

Anyone know the reason, or found a way to avoid or trap these unnecessary calls?

Thanks,
Robb

Robb Shecter <rs@onsitetech.com> writes:

When I create a table, I'm finding that when the table sets itself up,
GetValue is called for every cell, before the table is even visible.
When the table is running, GetValue does act normally.

IIRC I had a similar problem when AutoSizeColumns is set.

  Bernhard

路路路

--
Intevation GmbH http://intevation.de/
Sketch http://sketch.sourceforge.net/
MapIt! http://www.mapit.de/

Bernhard Herzog wrote:

Robb wrote:

...GetValue is called for every cell, before the table is even visible.
When the table is running, GetValue does act normally.
   
IIRC I had a similar problem when AutoSizeColumns is set.

FWIW, I wrote a hack/workaround based on the fact that GetValue(0,0) is called twice.

路路路

Robb Shecter <rs@onsitetech.com> writes:

Bernhard Herzog wrote:

>Robb wrote:
>
>>...GetValue is called for every cell, before the table is even visible.
>>When the table is running, GetValue does act normally.
>>
>
>IIRC I had a similar problem when AutoSizeColumns is set.
>
FWIW, I wrote a hack/workaround based on the fact that GetValue(0,0) is
called twice.

What does it do?

  Bernhard

路路路

--
Intevation GmbH http://intevation.de/
Sketch http://sketch.sourceforge.net/
MapIt! http://www.mapit.de/

Bernhard Herzog wrote:

What does it do?

Bernhard

In __init__, I set a flag to zero:

        self.zeroZeroReqs = 0

And in GetValue, I start with the following code:

        if self.zeroZeroReqs < 2:
            if row == 0 and col == 0:
                self.zeroZeroReqs += 1
            return ''

...Works fine for me.