[Newbie] Why doesn't this draw on my wxPython DC?

Hi,

(I have posted this to news:comp.lang.python too)

I am trying to learn how to use wxPython and currently I am especially
working with 'device contexts'.

By looking at (copy-pasting from) the examples and the pysketch sample
I am able to draw something, but I don't really get the understanding
of what is going on.

I have boiled my app down to simplest possible I think. It looks like:

···

================================================================
from wxPython.wx import *

class MyCanvas(wxScrolledWindow):
     def __init__(self, parent, id = -1, size = wxDefaultSize):
         wxScrolledWindow.__init__(self, parent, id, wxPoint(0, 0), size, wxSUNKEN_BORDER)

         self.buffer=wxEmptyBitmap(100,100)
         dc=wxBufferedDC(None, self.buffer)
         dc.SetBackground(wxBrush(self.GetBackgroundColour()))
         dc.Clear()
         dc.BeginDrawing()
         dc.DrawRectangle(10,10,100,100)
         dc.EndDrawing()

         EVT_PAINT(self,self.OnPaint)

     def OnPaint(self,event):
         dc = wxBufferedPaintDC(self,self.buffer)

app=wxPySimpleApp()
frame=wxFrame(None,-1,"Test")
win = MyCanvas(frame)
frame.Show(true)
app.MainLoop()

and draws a rectangle on the window. I could go on living happily, but
I would like to know why do I need a DC in both __init__ and OnPaint.

Why do I need to have the EVT_PAINT when the docs say:
"To draw on a window from outside OnPaint, construct a wxClientDC
object."?
Removing the event and changing the dc line in __init__ to use a
wxClientDC does not draw anything.

What are the arguments I feed to the DC's. Buffered DC is not
mentioned anywhere in the doc AFAICS.

Another question is what is the difference between the different
wxDC's (Paint, Client, Buffered, BufferedPaint,...)?

Could I do it without the class MyCanvas and completely procedural? I
believe not as I can't figure out what arguments to give to the
EVT_PAINT and the OnPaint if not self from a class. Does the script
itself have a 'self'?

Now when I have got it running I would like to be able to click on
items on the window, open an entry and write the input on this on the
DC.
I make a left down event reacting with:
     def OnLeftDown(self,event):
         dlg = wxTextEntryDialog(frame,"Write here","Entry","")
         dlg.ShowModal()

I then would like to draw the output from dlg.GetValue() on the DC,
but a new wxBufferedDC written exactly as in __init__ doesn't draw. Why?

A lot of questions which I hope someone will give me some information
on.

tia,
--
Brian Elmegaard (be@mek.dtu.dk)
Dept. of Mechanical Engineering, Energy Engineering,
Technical University of Denmark, Phone +45 4525 4169 Fax +45 4593 5215
http://www.et.dtu.dk/staff/be

Brian Elmegaard wrote:

and draws a rectangle on the window. I could go on living happily, but
I would like to know why do I need a DC in both __init__ and OnPaint.

You don't. For simple cases like this the following works just fine too:

class MyCanvas(wxScrolledWindow):
     def __init__(self, parent, id = -1, size = wxDefaultSize):
         wxScrolledWindow.__init__(self, parent, id, wxPoint(0, 0), size, wxSUNKEN_BORDER)
         EVT_PAINT(self,self.OnPaint)

     def OnPaint(self,event):
         dc = wxPaintDC(self)
         dc.BeginDrawing()
         dc.SetBackground(wxBrush(self.GetBackgroundColour()))
         dc.Clear()
         dc.DrawRectangle(10,10,100,100)
         dc.EndDrawing()

Why do I need to have the EVT_PAINT when the docs say:
"To draw on a window from outside OnPaint, construct a wxClientDC
object."?

Whenever a portion of the window needs to be refreshed because it was just uncovered for whatever reason (resizing, another window moves across it, etc.) then the platform sends the EVT_PAINT event to allow the window to redraw itself. You need to respond to this event to keep the contents of your window up to date.

Removing the event and changing the dc line in __init__ to use a
wxClientDC does not draw anything.

Because at the time of __init__ the window is not visible, so what is drawn then is not seen. Later when the frame is shown the canvas window is shown and gets an EVT_PAINT event.

What are the arguments I feed to the DC's. Buffered DC is not
mentioned anywhere in the doc AFAICS.

wxBufferedDC( wxDC *dc, const wxBitmap &buffer );
wxBufferedPaintDC( wxWindow *window, const wxBitmap &buffer = wxNullBitmap );

Another question is what is the difference between the different
wxDC's (Paint, Client, Buffered, BufferedPaint,...)?

wxPaintDC can only be used inside a EVT_PAINT event and has extra info about what regons of the window have been damaged and need refreshed, allowing you to optimize your redraw if you want.

wxClientDC can be used anytime for drawing to the window, but you must ensure that whatever you draw then is also redrawn in a EVT_PAINT event otherwise it will get lost the next time the window is refreshed.

wxMemoryDC allows you to draw to a wxBitmap instead of to a window.

wxBufferedDC and wxBufferedPaintDC are simple convenience classes that derive from wxMemoryDC. You give it a buffer bitmap and then all drawing goes to the bitmap. When the last reference to the buffered dc goes out of scope or is deleted then the contents of the bitmap are dumped to the real DC (if any) that you gave to the buffered dc when you constructed it. This helps to reduce flicker and such for complex drawings. and also reduces the time needed for EVT_PAINT handlers because all they are doing is drawing the buffer bitmap, not a whole complex drawing. You can easily do the same thing yourself without the buffered DCs with just a wxMemoryDC, a bitmap and a DrawBitmap call.

Could I do it without the class MyCanvas and completely procedural? I
believe not as I can't figure out what arguments to give to the
EVT_PAINT and the OnPaint if not self from a class. Does the script
itself have a 'self'?

Using a class would be a better code organization, but it is not strictly necessary. All event handlers are called with a single event parameter, it's only the fact that they are normally instance methods that requires the extra self arg.

Now when I have got it running I would like to be able to click on
items on the window, open an entry and write the input on this on the
DC.
I make a left down event reacting with:
    def OnLeftDown(self,event):
        dlg = wxTextEntryDialog(frame,"Write here","Entry","")
        dlg.ShowModal()

I then would like to draw the output from dlg.GetValue() on the DC,
but a new wxBufferedDC written exactly as in __init__ doesn't draw. Why?

Because in __init__ you dont give it a real DC to draw to when done, so it only draws to the bitmap. If you do it like this then it should work:
         dc=wxBufferedDC(wxClientDC(self), self.buffer)

···

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

Just a few more comments to add to Robin's thorough answer..

By the way, I think this brief disscussion should be in a FAQ somewhere,
or maybe the wiki. Robin, I don't imagine you'll mind if I find a place
for it in the Wiki, but in case you do, let me know soon!

Robin Dunn wrote:

wxPaintDC can only be used inside a EVT_PAINT event and has extra info
about what regons of the window have been damaged and need refreshed,
allowing you to optimize your redraw if you want.

Some of that optimization happens anyway. I've noticed that when I
DC.DrawBitmap inside and EVT_PAINT, only the damaged portion is
re-drawn.

wxBufferedDC and wxBufferedPaintDC are simple convenience classes that
derive from wxMemoryDC.

...

You can easily do the same thing yourself without the

buffered DCs with just a wxMemoryDC, a bitmap and a DrawBitmap call.

See: http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing

in the Wiki for an example of a double buffered window class done both
with and without wxBufferedDC

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                        
NOAA/OR&R/HAZMAT (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

Chris Barker wrote:

By the way, I think this brief disscussion should be in a FAQ somewhere,
or maybe the wiki.

I've just added it to the FAQ in teh Wiki. Could everyone please take a
look and add and correct as needed.

http://wiki.wxpython.org/index.cgi/Frequently_20Asked_20Questions

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                        
NOAA/OR&R/HAZMAT (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

Dear Robin and Chris,

I really appreciate your informative replies. I am now able to understand just a bit of what is going on. I will work on getting productive in wx.

Thanks a lot,
Brian

Chris Barker wrote:

···

Chris Barker wrote:

By the way, I think this brief disscussion should be in a FAQ somewhere,
or maybe the wiki.

I've just added it to the FAQ in teh Wiki. Could everyone please take a
look and add and correct as needed.

http://wiki.wxpython.org/index.cgi/Frequently_20Asked_20Questions

-Chris

--
Brian Elmegaard (be@mek.dtu.dk)
Dept. of Mechanical Engineering, Energy Engineering,
Technical University of Denmark, Phone +45 4525 4169 Fax +45 4593 5215
http://www.et.dtu.dk/staff/be