wxWidgets绘图基础
1. 实现窗口
wxWidgets窗口程序需要四个必须的部分:
- 添加一个继承
wxApp
的应用程序类。 - 添加一个继承
wxFrame
的框架类。 - 重载
wxApp::OnInit()
成员函数,并在其中创建框架类的对象。 - 在调用宏定义
IMPLEMENT_APP()
实例化应用程序。
#include <wx/wx.h>
#include <cstdlib>
// 框架类
class Tetris : public wxFrame {
public:
Tetris(const wxString &title)
:wxFrame(NULL, wxID_ANY, title) {}
}
win10
2. 面板
所有的绘制都在面板上。并且在框架中添加面板类。
- 添加一个继承
wxPanel
的应用程序类。 - 在框架类的构造函数中创建面板类的对象。
class Board : public wxPanel {
public:
Board(wxFrame *parent): wxPanel(parent){
}
};
class Tetris : public wxFrame {
public:
Tetris(const wxString &title)
:wxFrame(NULL, wxID_ANY, title) {
Board *board = new Board(this);
}
};
3. 绘制事件
通常GUI程序绘制图像,是使用绘制事件通知的方式界面(面板)绘制的。这是一个经典的MVC模式。
一般在如下几种情况会触发绘制事件:
- 程序初始化界面显示。
- 窗口最小化后再重新出现。
- 窗口尺寸变化。
- 窗口被挡住后,重新出现。
- 代码调用
Refresh()
或ReflashRect()
。
为了能够处理绘制事件,我们需要给它绑定一个绘制事件。
class Board : public wxPanel {
public:
Board(wxFrame *parent): wxPanel(parent){
// 绑定绘制事件
Bind(wxEVT_PAINT, &Board::OnPaint,this);
}
// 定义绘制函数
void OnPaint(wxPaintEvent &event) {
}
};
其中,wxPaintEvent
是绘制事件类,它包含了一些绘制信息。wxEVT_PAINT
是绘制事件宏定义表示事件类型。
执行效果与上图一样,因为我们什么也没有绘制。
4. 绘制原理
4.1 绘制图形
计算机绘制图像与人类画画有很多相似的地方。也需要画板、绘图工具(笔、刷子)。面板Panel就是我们的画板,绘图工具在程序中被称为设备上下文(Device Context,简称DC)。DC提供了绘制各种图像的方法。
在wxWidgets常用wxPaintDC
绘制图像。它可以绘制各种图像。例如:直线、矩形、
No. | 函数 | 功能 |
---|---|---|
1 | void DrawPoint (const wxPoint &pt) |
画点(像素) |
2 | void DrawLine (const wxPoint &pt1, const wxPoint &pt2) |
画线 |
3 | void DrawRectangle (const wxPoint &pt, const wxSize &sz) |
画矩形 |
4 | void DrawPolygon (int n, const wxPoint points[]) |
画多边形 |
5 | void DrawCircle (const wxPoint &pt, wxCoord radius) |
画圆 |
6 | void DrawText (const wxString &text, const wxPoint &pt) |
绘制文本 |
说明
-
wxPoint
表示一个二维坐标的点,构造函数是wxPoint(int x,int y)
。 -
wxCoord
表示一个数字值,原型是整形int
。 -
wxSize
表示宽高,构造函数是wxPoint(int width,int height)
。
注意,屏幕使用的坐标系被称为屏幕坐标系,默认的坐标原点在屏幕左上角。与我们数学中使用的笛卡尔坐标系是有区别的。
// 定义绘制函数
void OnPaint(wxPaintEvent &event) {
wxPaintDC dc(this);
// 画点
dc.DrawPoint(wxPoint(10, 10));
// 画线
dc.DrawLine(wxPoint(15, 15), wxPoint(75, 75));
// 画矩形
dc.DrawRectangle(wxPoint(80, 35),wxSize(50,45));
// 画多边形
const wxPoint points[] = {
wxPoint(120,120),
wxPoint(120,160),
wxPoint(140,160)
};
dc.DrawPolygon(3,points);
// 画圆
dc.DrawCircle(wxPoint(280,100),80);
// 画文字
dc.DrawText(wxT("测试文字"), wxPoint(200, 160));
}
注意:如果两个图形存在重合的部分,后面绘制的会覆盖前面绘制的。
4.2 设置绘制
上面的图形都是黑线白底,线的宽度是1个像素,可以通过下面的方式修改。
No. | 函数 | 功能 |
---|---|---|
1 | void SetPen (const wxPen &pen) |
设置画笔 |
2 | void SetBrush (const wxBrush &brush) |
设置画刷 |
3 | void SetFont (const wxFont &font) |
设置字体 |
- 设置画笔
// 定义绘制函数 void OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); // 画点 dc.SetPen(*wxRED_PEN); dc.DrawPoint(wxPoint(10, 10)); // 画线 dc.SetPen(*wxBLUE_PEN); dc.DrawLine(wxPoint(15, 15), wxPoint(75, 75)); // 画矩形 dc.SetPen(*wxGREEN_PEN); dc.DrawRectangle(wxPoint(80, 35),wxSize(50,45)); // 画多边形 const wxPoint points[] = { wxPoint(120,120), wxPoint(120,160), wxPoint(140,160) }; dc.SetPen(wxColour(255,255,0)); dc.DrawPolygon(3,points); // 画圆 dc.SetPen(wxColour(59, 128, 59)); dc.DrawCircle(wxPoint(280,100),80); // 画文字 dc.DrawText(wxT("测试文字"), wxPoint(200, 160)); }
说明
颜色设置有两种方式
- 使用内置的宏定义。例如:
wxRED_PEN
,wxGREEN_PEN
,wxBLUE_PEN
等。
详细可参考帮助手册。
- 使用
wxColour
定义颜色,使用RGB模型。
注意
SetPen()
不能改变文字的颜色,需要下面的使用SetFont()
。
- 设置画刷
// 定义绘制函数 void OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); // 画点 dc.SetPen(*wxRED_PEN); dc.DrawPoint(wxPoint(10, 10)); // 画线 dc.SetPen(*wxBLUE_PEN); dc.DrawLine(wxPoint(15, 15), wxPoint(75, 75)); // 画矩形 dc.SetPen(*wxGREEN_PEN); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(wxPoint(80, 35),wxSize(50,45)); // 画多边形 const wxPoint points[] = { wxPoint(120,120), wxPoint(120,160), wxPoint(140,160) }; dc.SetPen(wxColour(255,255,0)); dc.SetBrush(wxColour(59, 128, 59)); dc.DrawPolygon(3,points); // 画圆 dc.SetPen(wxColour(59, 128, 59)); dc.SetBrush(wxColour(255, 255, 0)); dc.DrawCircle(wxPoint(280,100),80); // 画文字 dc.DrawText(wxT("测试文字"), wxPoint(200, 160)); }
颜色设置方式与画笔设置方式相同。
- 设置字体
// 画文字 dc.SetTextForeground(wxColour(255,0 , 255)); // 设置字体颜色 dc.SetFont(wxFontInfo(12).Bold(2).FaceName(wxT("MS yahei"))); // 设置字体大小,粗细,字体 dc.DrawText(wxT("测试文字"), wxPoint(200, 160));
设置字体颜色要使用SetTextForeground()
方法。
4.3 绘制图片
绘制图片需要用到如下两个函数:
No. | 函数 | 功能 |
---|---|---|
1 | void wxInitAllImageHandlers() |
初始化图片处理器 |
2 | void DrawBitmap (const wxBitmap &bmp, const wxPoint &pt) |
绘制图片 |
- 示例
// 绘制图片 wxInitAllImageHandlers(); dc.DrawBitmap(wxBitmap(wxT("logo.png"),wxBITMAP_TYPE_ANY),wxPoint(30,30));
5 鼠标事件
界面上的图像有一部分使用代码生成,也有一部分使用鼠标创建。添加鼠标事件方式与绘制事件一样。需要新建鼠标处理函数并且绑定到事件中。但是鼠标事件种类要比绘图事件多,常用的有如下几个。
No. | 事件 | 触发动作 |
---|---|---|
1 | wxEVT_LEFT_DOWN |
鼠标左键按下 |
2 | wxEVT_LEFT_UP |
鼠标左键弹起 |
3 | wxEVT_LEFT_DCLICK |
鼠标左键双击 |
4 | wxEVT_MIDDLE_DOWN |
鼠标中键按下 |
5 | wxEVT_MIDDLE_UP |
鼠标中键弹起 |
6 | wxEVT_MIDDLE_DCLICK |
鼠标中键双击 |
7 | wxEVT_RIGHT_DOWN |
鼠标右键按下 |
8 | wxEVT_RIGHT_UP |
鼠标右键弹起 |
9 | wxEVT_RIGHT_DCLICK |
鼠标右键双击 |
10 | wxEVT_MOVE |
鼠标移动 |
- 鼠标移动
#include <wx/wx.h>
class Move : public wxFrame {
public:
Move(const wxString& title): wxFrame(NULL, wxID_ANY, title) {
wxPanel* panel = new wxPanel(this, -1);
st1 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 10));
st2 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 30));
panel->Bind(wxEVT_MOTION,&Move::OnMove,this);
}
void OnMove(wxMouseEvent & event){
wxPoint size = event.GetPosition();
st1->SetLabel(wxString::Format(wxT("x: %d"), size.x ));
st2->SetLabel(wxString::Format(wxT("y: %d"), size.y ));
event.Skip();
Update();
}
private:
wxStaticText *st1;
wxStaticText *st2;
};
class MyApp : public wxApp {
public:
virtual bool OnInit(){
Move *move = new Move(wxT("Move event"));
move->Show(true);
return true;
}
};
IMPLEMENT_APP(MyApp)
- 鼠标画线
class Board : public wxPanel {
public:
Board(wxFrame *parent): wxPanel(parent){
// 绑定绘制事件
Bind(wxEVT_PAINT, &Board::OnPaint,this);
// 绑定鼠标事件
Bind(wxEVT_LEFT_DOWN, &Board::OnLeftDown,this);
Bind(wxEVT_LEFT_UP, &Board::OnLeftUp,this);
}
// 定义绘制函数
void OnPaint(wxPaintEvent &event) {
wxPaintDC dc(this);
dc.DrawLine(startPos,endPos);
}
// 定义鼠标左键按下函数
void OnLeftDown(wxMouseEvent &event) {
startPos = event.GetPosition();
Refresh();
}
// 定义鼠标右键弹起函数
void OnLeftUp(wxMouseEvent &event) {
endPos = event.GetPosition();
Refresh();
}
private:
wxPoint startPos;
wxPoint endPos;
};
在wxMouseEvent
中包含了一个很重要的信息--事件出发时鼠标的位置。可以通过调用事件的方法GetPosition()
获取。
尝试加上鼠标移动事件,看看会有什么效果?
void OnMove(wxMouseEvent &event) {
endPos = event.GetPosition();
Refresh();
}
6 键盘事件
class Board : public wxPanel {
public:
Board(wxFrame *parent): wxPanel(parent){
// 绑定绘制事件
Bind(wxEVT_PAINT, &Board::OnPaint,this);
// 绑定键盘事件
Bind(wxEVT_KEY_DOWN,&Board::OnKeyDown,this);
startPos = wxPoint(10,10);
endPos = wxPoint(50,50);
}
// 定义绘制函数
void OnPaint(wxPaintEvent &event) {
wxPaintDC dc(this);
dc.DrawLine(startPos,endPos);
}
void OnKeyDown(wxKeyEvent &event) {
case WXK_RIGHT:
startPos.x += 10;
endPos.x += 10;
break;
case WXK_LEFT:
startPos.x -= 10;
endPos.x -= 10;
break;
case WXK_UP:
startPos.y -= 10;
endPos.y -= 10;
break;
case WXK_DOWN:
startPos.y += 10;
endPos.y += 10;
break;
default:
break;
}
Refresh();
}
private:
wxPoint startPos;
wxPoint endPos;
};
7 定时器事件
class Board : public wxPanel {
public:
Board(wxFrame *parent): wxPanel(parent){
// 绑定绘制事件
Bind(wxEVT_PAINT, &Board::OnPaint,this);
// 绑定事件
Bind(wxEVT_TIMER,&Board::OnTimer,this);
startPos = wxPoint(150,50);
endPos = wxPoint(50,50);
timer = new wxTimer(this,wxID_ANY);
timer->Start(1000);
}
// 定义绘制函数
void OnPaint(wxPaintEvent &event) {
wxPaintDC dc(this);
dc.DrawLine(startPos,endPos);
}
void OnTimer(wxTimerEvent &event) {
static int i = 0;
i++;
i%=60;
double angle = M_PI/30*i;
startPos = endPos + wxPoint(100*cos(angle),100*sin(angle));
Refresh();
}
private:
wxPoint startPos;
wxPoint endPos;
wxTimer* timer;
};