[wxPython] buffered window thoughts and sample code

I spent a lot of time this weekend thinking about what it was that I needed
for PythonCard and how it might vary from the current PythonCard
BitmapCanvas class and whether it made any sense to include a buffered
window in wxPython. After doing the initial code I think that this would be
a nice addition to wxPython, but given how the current implementation works
I doubt there would be any benefits to doing the code in C++. Most of the
overhead are in method wrappers and of course the Blit operations.

This thread is not about providing an object canvas for wxPython like the
FloatCanvas Chris posted earlier, that is a separate subject.

I've attached a zip that contains an implementation of a BufferedWindow
along with a script to test the class. The class isn't complete, but enough
of it is implemented to give us something to discuss. I'm not tied to the
implementation I've provided, there is almost certainly a better way. Robin,
will probably have an aha moment and explain a better way than my naive
code. :wink:

The test app also shows one way you can include a PyCrust shell in your own
application, which is something PythonCard does by default. When the test
script starts up, it automatically runs the pycrustrc.py file, so it is easy
to save some typing and put commands you want to repeat in the pycrustrc.py
file. While the app is running, you can interactively draw in the window
using the shell.

Here are some of the thoughts that led up to the code in no particular
order:

- follow the wxDC API

- maintain a persistent bitmap

- all drawing is done offscreen

- the method bound to EVT_PAINT should simply take the data in the offscreen
buffer and blit it to the onscreen window.

- allow for resizing the persistent bitmap automatically via a method bound
to EVT_SIZE

- want to be able to use the buffered window interactively with a minimum of
hassle. That means that when you make a call such as DrawLine the screen
should be updated automatically without the user having to call Blit or a
special Refresh method.
- In addition, you shouldn't have to create a wxMemoryDC every time you want
to do a drawing operation, set up the brush, pen, etc. so I'm assuming a
wxMemoryDC or some equivalent will keep the state.

- Also, want to be able to do a series of drawing operations without
updating the screen in order to improve performance when needed and then
Blit/Refresh the window when needed. So, the default would be to blit
automatically, but that could be toggled via a flag.

- if someone wanted to provide an alternative drawing API (say piddle or
reportlab instead of wxDC) they would likely use the same kind of wrapping,
but just use different method names and arguments.

- can't subclass wxMemoryDC unless there is a way to replace the selected
bitmap. Perhaps having a resize method could work?

- BeginDrawing and EndDrawing shouldn't be necessary because the drawing is
always done to the offscreen bitmap and then Blit is used to move the
resulting image onscreen.

- another possibility is using a wxMemoryDC as a delegate

- Unless wxWindows has improved the methods that accumulate the "dirty
region" that needs to be updated after each drawing call, just blit the
entire bitmap since MinX(), MinY(), MaxX(), MaxY(), etc. don't appear to
take into consideration the pen thickness, brush, cap style, or other items
that impact the dirty region. Blitting the entire offscreen buffer is
obviously less efficient, but will always work.

- it would be nice if this control could be subclassed to make custom
controls or used as part of a compound control where you needed more
elaborate drawing or needed to avoid flicker that might occur due to OnPaint
causing a redraw of the entire control Something like the calendar controls
come to mind, though it is possible that by using Freeze() and Thaw()
flicker can be eliminated in many cases?!

I thought about making a subclass or possibly a mixin class for use with
wxStaticBitmap and wxBitmapButton, so that changes to the offscreen buffer
could automatically be reflected in the wxBitmap used onscreen in the
control, but I think the ownership of the bitmap might be an issue. Another
problem might be how masks are handled, but since it is probably logical to
want to be able to use masks and transparency so that the window can "float"
over a panel or other controls, this is worth investigating.

ka

BufWinTest-2002-04-01.zip (4.8 KB)

ยทยทยท

---
Kevin Altis
altis@semi-retired.com

---
readme.txt

BufferedWindow test

You can put code you want to run in the pycrustrc.py file and it
will be run automatically when testBufferedWindow.py is run. You
can also draw in the window interactively using the variable w
as a shortcut:

w.DrawLine(1, 1, 50, 50)
w.autoRefresh = 0
w.SetForegroundColour('blue')
w.DrawEllipse(50, 50, 40, 40)
w.Refresh()
w.autoRefresh = 1

I think I implemented all of the wxDC methods that actually draw
something, but I have not implemented the entire wxDC API for
this test.

Kevin Altis
2002-04-01