Difference between revisions of "Drawing on a panel with a DC"
Jump to navigation
Jump to search
Line 1: | Line 1: | ||
+ | __TOC__ | ||
+ | |||
== Snippet == | == Snippet == | ||
Revision as of 18:00, 21 May 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.
- 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)