Difference between revisions of "Drawing on a panel with a DC"

From WxWiki
Jump to navigation Jump to search
Line 162: Line 162:
 
* You ''need'' to handle the paint event, and it must be able to redraw the whole drawing at any time. This is because you window manager may throw away your drawing at any time and ask you to draw it again later through a paint event
 
* You ''need'' to handle the paint event, and it must be able to redraw the whole drawing at any time. This is because you window manager may throw away your drawing at any time and ask you to draw it again later through a paint event
 
* Use a wx[Buffered]PaintDC in paint events, and a wxClientDC outside paint events.
 
* Use a wx[Buffered]PaintDC in paint events, and a wxClientDC outside paint events.
 +
* If you do catch the paint event, you ''must'' create a wx[Buffered]PaintDC in it, even if you don't use it, letting the paint event unhandled can cause weird behaviour
 
* Do not store the created DC or keep it for later in any way.
 
* Do not store the created DC or keep it for later in any way.
  

Revision as of 09:31, 19 June 2010

Snippet

If you want to do drawing using software rendering on a wxPanel, this code snippet should get you started :

#include "wx/wx.h"
#include "wx/sizer.h"

class BasicDrawPane : public wxPanel
{
    
public:
    BasicDrawPane(wxFrame* parent);
    
    void paintEvent(wxPaintEvent & evt);
    void paintNow();
    
    void render(wxDC& dc);
    
    // some useful events
    /*
     void mouseMoved(wxMouseEvent& event);
     void mouseDown(wxMouseEvent& event);
     void mouseWheelMoved(wxMouseEvent& event);
     void mouseReleased(wxMouseEvent& event);
     void rightClick(wxMouseEvent& event);
     void mouseLeftWindow(wxMouseEvent& event);
     void keyPressed(wxKeyEvent& event);
     void keyReleased(wxKeyEvent& event);
     */
    
    DECLARE_EVENT_TABLE()
};


class MyApp: public wxApp
{
    bool OnInit();
    
    wxFrame *frame;
    BasicDrawPane * drawPane;
public:
        
};

IMPLEMENT_APP(MyApp)


bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600));
	
    drawPane = new BasicDrawPane( (wxFrame*) frame );
    sizer->Add(drawPane, 1, wxEXPAND);
	
    frame->SetSizer(sizer);
    frame->SetAutoLayout(true);
	
    frame->Show();
    return true;
} 

BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
// some useful events
/*
 EVT_MOTION(BasicDrawPane::mouseMoved)
 EVT_LEFT_DOWN(BasicDrawPane::mouseDown)
 EVT_LEFT_UP(BasicDrawPane::mouseReleased)
 EVT_RIGHT_DOWN(BasicDrawPane::rightClick)
 EVT_LEAVE_WINDOW(BasicDrawPane::mouseLeftWindow)
 EVT_KEY_DOWN(BasicDrawPane::keyPressed)
 EVT_KEY_UP(BasicDrawPane::keyReleased)
 EVT_MOUSEWHEEL(BasicDrawPane::mouseWheelMoved)
 */

// catch paint events
EVT_PAINT(BasicDrawPane::paintEvent)

END_EVENT_TABLE()


// some useful events
/*
 void BasicDrawPane::mouseMoved(wxMouseEvent& event) {}
 void BasicDrawPane::mouseDown(wxMouseEvent& event) {}
 void BasicDrawPane::mouseWheelMoved(wxMouseEvent& event) {}
 void BasicDrawPane::mouseReleased(wxMouseEvent& event) {}
 void BasicDrawPane::rightClick(wxMouseEvent& event) {}
 void BasicDrawPane::mouseLeftWindow(wxMouseEvent& event) {}
 void BasicDrawPane::keyPressed(wxKeyEvent& event) {}
 void BasicDrawPane::keyReleased(wxKeyEvent& event) {}
 */

BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
}

/*
 * Called by the system of by wxWidgets when the panel needs
 * to be redrawn. You can also trigger this call by
 * calling Refresh()/Update().
 */

void BasicDrawPane::paintEvent(wxPaintEvent & evt)
{
    wxPaintDC dc(this);
    render(dc);
}

/*
 * Alternatively, you can use a clientDC to paint on the panel
 * at any time. Using this generally does not free you from
 * catching paint events, since it is possible that e.g. the window
 * manager throws away your drawing when the window comes to the
 * background, and expects you will redraw it when the window comes
 * back (by sending a paint event).
 */
void BasicDrawPane::paintNow()
{
    wxClientDC dc(this);
    render(dc);
}

/*
 * Here we do the actual rendering. I put it in a separate
 * method so that it can work no matter what type of DC
 * (e.g. wxPaintDC or wxClientDC) is used.
 */
void BasicDrawPane::render(wxDC&  dc)
{
    // draw some text
    dc.DrawText(wxT("Testing"), 40, 60); 
    
    // draw a circle
    dc.SetBrush(*wxGREEN_BRUSH); // green filling
    dc.SetPen( wxPen( wxColor(255,0,0), 5 ) ); // 5-pixels-thick red outline
    dc.DrawCircle( wxPoint(200,100), 25 /* radius */ );
    
    // draw a rectangle
    dc.SetBrush(*wxBLUE_BRUSH); // blue filling
    dc.SetPen( wxPen( wxColor(255,175,175), 10 ) ); // 10-pixels-thick pink outline
    dc.DrawRectangle( 300, 100, 400, 200 );
    
    // draw a line
    dc.SetPen( wxPen( wxColor(0,0,0), 3 ) ); // black line, 3 pixels thick
    dc.DrawLine( 300, 100, 700, 300 ); // draw line across the rectangle
    
    // Look at the wxDC docs to learn how to draw other stuff
}

Important Notes

  • You need to handle the paint event, and it must be able to redraw the whole drawing at any time. This is because you window manager may throw away your drawing at any time and ask you to draw it again later through a paint event
  • Use a wx[Buffered]PaintDC in paint events, and a wxClientDC outside paint events.
  • If you do catch the paint event, you must create a wx[Buffered]PaintDC in it, even if you don't use it, letting the paint event unhandled can cause weird behaviour
  • Do not store the created DC or keep it for later in any way.

To avoid flickering

  • Try bufferred DCs, they are necessary on some platforms. wxBufferedDC and wxAutoBufferedPaintDC might help you.
  • Call ->SetDoubleBuffered(true); on the panel (On Windows only?)
  • Call SetBackgroundStyle(wxBG_CUSTOM) ( See this : http://docs.wxwidgets.org/stable/wx_wxwindow.html#wxwindowsetbackgroundstyle )
    • On wx 2.9.1+, wxBG_STYLE_PAINT can help too and is clearer than "custom"
    • Or, alternatively, Catch the wxEVT_ERASE_BACKGROUND event

For all details, see article Flicker-Free Drawing. This generally does not happen on systems with composition (OS X, Windows Vista/7, linux with compositor installed)