WxWidgets:DeviceContexts
Draw Text
void SVMMediaPlayer::drawFireText(wxDC * dc)
{
wxColour colour("black");
dc->SetTextForeground(colour);
dc->DrawText(wxT("Hello, world!"), 0, 0);
}
Draw Rect example
void SVMMediaPlayer::drawRect(wxDC * dc, wxPen & pen, int x, int y, int width, int height)
{
// defend of reallocate (static).
// {
static wxRect rect;
static wxPointList point_list;
static wxPoint p0, p1, p2, p3;
// }
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
p0 = rect.GetBottomLeft();
p1 = rect.GetBottomRight();
p2 = rect.GetTopRight();
p3 = rect.GetTopLeft();
point_list.Clear();
point_list.Append(&p0);
point_list.Append(&p1);
point_list.Append(&p2);
point_list.Append(&p3);
point_list.Append(&p0);
assert(dc != NULL);
dc->SetPen(pen);
dc->DrawLines(&point_list);
}
Render loop
- [추천] Making a render loop
Using idle events
Pros:
- doesn't hog the system, as it renders only when it can
- one of the fastest ways possible in wx (on my MacBook 2GHz I get around 3500 fps with the code below)
Cons:
- framerate is not necessarly regular
- can stop rendering when you do something else, for instance open a menu
#include "wx/sizer.h"
#include "wx/wx.h"
class BasicDrawPane : public wxPanel
{
public:
BasicDrawPane(wxFrame* parent);
void paintEvent(wxPaintEvent& evt);
void paintNow();
void render( wxDC& dc );
DECLARE_EVENT_TABLE()
};
class MyFrame;
class MyApp: public wxApp
{
bool render_loop_on;
bool OnInit();
void onIdle(wxIdleEvent& evt);
MyFrame* frame;
BasicDrawPane* drawPane;
public:
void activateRenderLoop(bool on);
};
IMPLEMENT_APP(MyApp)
class MyFrame : public wxFrame
{
public:
MyFrame() : wxFrame((wxFrame *)NULL, -1, wxT("Hello wxDC"), wxPoint(50,50), wxSize(400,200))
{
}
void onClose(wxCloseEvent& evt)
{
wxGetApp().activateRenderLoop(false);
evt.Skip(); // don't stop event, we still want window to close
}
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_CLOSE(MyFrame::onClose)
END_EVENT_TABLE()
bool MyApp::OnInit()
{
render_loop_on = false;
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
frame = new MyFrame();
drawPane = new BasicDrawPane( frame );
sizer->Add(drawPane, 1, wxEXPAND);
frame->SetSizer(sizer);
frame->Show();
activateRenderLoop(true);
return true;
}
void MyApp::activateRenderLoop(bool on)
{
if(on && !render_loop_on)
{
Connect( wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle) );
render_loop_on = true;
}
else if(!on && render_loop_on)
{
Disconnect( wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle) );
render_loop_on = false;
}
}
void MyApp::onIdle(wxIdleEvent& evt)
{
if(render_loop_on)
{
drawPane->paintNow();
evt.RequestMore(); // render continuously, not only once on idle
}
}
BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
EVT_PAINT(BasicDrawPane::paintEvent)
END_EVENT_TABLE()
BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
}
void BasicDrawPane::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
render(dc);
}
void BasicDrawPane::paintNow()
{
wxClientDC dc(this);
render(dc);
}
void BasicDrawPane::render( wxDC& dc )
{
static int y = 0;
static int y_speed = 2;
y += y_speed;
if(y<0) y_speed = 2;
if(y>200) y_speed = -2;
dc.SetBackground( *wxWHITE_BRUSH );
dc.Clear();
dc.DrawText(wxT("Testing"), 40, y);
}
Using a timer
Pros:
- Regular framerate
- Doesn't stop like idle events when you do something else, like open a menu
Cons:
- Not as fast as idle events
- Implementation quality depends on your platform; some platforms provide better timers than others.
#include <wx/sizer.h>
#include <wx/wx.h>
#include <wx/timer.h>
class BasicDrawPane;
class RenderTimer : public wxTimer
{
BasicDrawPane* pane;
public:
RenderTimer(BasicDrawPane* pane);
void Notify();
void start();
};
class BasicDrawPane : public wxPanel
{
public:
BasicDrawPane(wxFrame* parent);
void paintEvent(wxPaintEvent& evt);
void paintNow();
void render( wxDC& dc );
DECLARE_EVENT_TABLE()
};
class MyFrame;
class MyApp: public wxApp
{
bool OnInit();
MyFrame* frame;
public:
};
RenderTimer::RenderTimer(BasicDrawPane* pane) : wxTimer()
{
RenderTimer::pane = pane;
}
void RenderTimer::Notify()
{
pane->Refresh();
}
void RenderTimer::start()
{
wxTimer::Start(10);
}
IMPLEMENT_APP(MyApp)
class MyFrame : public wxFrame
{
RenderTimer* timer;
BasicDrawPane* drawPane;
public:
MyFrame() : wxFrame((wxFrame *)NULL, -1, wxT("Hello wxDC"), wxPoint(50,50), wxSize(400,200))
{
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
drawPane = new BasicDrawPane( this );
sizer->Add(drawPane, 1, wxEXPAND);
SetSizer(sizer);
timer = new RenderTimer(drawPane);
Show();
timer->start();
}
~MyFrame()
{
delete timer;
}
void onClose(wxCloseEvent& evt)
{
timer->Stop();
evt.Skip();
}
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_CLOSE(MyFrame::onClose)
END_EVENT_TABLE()
bool MyApp::OnInit()
{
frame = new MyFrame();
frame->Show();
return true;
}
BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
EVT_PAINT(BasicDrawPane::paintEvent)
END_EVENT_TABLE()
BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
}
void BasicDrawPane::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
render(dc);
}
void BasicDrawPane::paintNow()
{
wxClientDC dc(this);
render(dc);
}
void BasicDrawPane::render( wxDC& dc )
{
static int y = 0;
static int y_speed = 2;
y += y_speed;
if(y<0) y_speed = 2;
if(y>200) y_speed = -2;
dc.SetBackground( *wxWHITE_BRUSH );
dc.Clear();
dc.DrawText(wxT("Testing"), 40, y);
}
Note: When using this method with wxMSW it is very important to include
Favorite site
- [추천] wxWiki: Flicker-Free Drawing
- Drawing and Printing in C++ with wxWidgets 1
- Double Buffering example
- Drawing on a panel with a DC
References
-
Drawing_and_Printing_in_C++_with_wxWidgets.pdf ↩