wxWidgets教程(4)- 绘图与自绘标题栏

wxDC

1、所有的绘图设备类都是继承自wxDC。

2、关于坐标系

默认的坐标原点在屏幕左上角,当然这是可以改变的,使用函数SetDeviceOrigin。

此函数仅改变当前dc的坐标原点,一般用于打印文稿的时候,设置打印设备的原点。

void SetDeviceOrigin(wxCoord x, wxCoord y)
类型wxCoord的原型是整形int,英文中是坐标的意思。

当然坐标系的方向也是可以改变的,使用以下函数:

void SetAxisOrientation(bool xLeftRight, bool yBottomUp)
第一个参数:为true时,从左向右,反之。。。

第二个参数:为true时,从下向上。

主要用途为股票趋势图这类和数学关系比较大的场合,这些要求左下角为原点,x轴向右,y轴向上。

3、绘图设备的大小(逻辑单位--像素与设备单位--毫米)

按照像素单位获取设备的大小:GetSize

按照毫米单位获取设备的大小:GetSizeMM

获取设备每英寸的像素密度ppi:GetPPI

获取设备每像素占的位宽:GetDepth

更改逻辑单位与设备单位的缩放比例:SetUserScale

	wxClientDC dc(this);
	dc.SetMapMode(wxMM_TEXT);
	dc.SetUserScale(1.0,1.0);
在wxMM_TEXT模式下,缩放比例1.0,1.0,可以将逻辑单位与设备单位等同。

4、区域绘图

所谓区域绘图,是指定一个区域,所有超过这个区域的范围都将被忽略。

一般情况下,在某个区域内不停的绘制文字时,会有重影的情况,需要不断擦除这个区域,重新绘制文字。

// 鼠标移动事件响应
void wxFontSelectorCtrl::OnMouseEvent(wxMouseEvent &event)
{
	wxClientDC dc(this);
	// 设置一个矩形区域
	dc.SetClippingRegion(wxPoint(0,0),wxSize(200,20));
	// 清除之前绘制的文字
	dc.Clear();
	// 设置文字的前景色
	dc.SetTextForeground(wxColour(255,255,0));
	// 设置文字的字体
	dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("宋体")));
	wxPoint pt = event.GetPosition();
	dc.DrawText(wxString::Format(wxT("当前坐标x-%d y-%d"), pt.x, pt.y),wxPoint(0,0));
	dc.SetFont(wxNullFont);
	// 销毁之前设置的区域
	dc.DestroyClippingRegion();
}

上面的绘图会有闪烁的情况,可以使用双缓冲绘图,代码如下:

	wxClientDC clientDC(this);
	// 设置一个矩形区域
	clientDC.SetClippingRegion(wxPoint(0, 0), wxSize(200, 20));

	wxBufferedDC dc(&clientDC);
	...

wxClientDC

客户区绘图设备DC,用来在非重绘的事件处理函数中使用,即除了EVT_PAINT和EVT_NC_PAINT事件之外的都可以。

例如,在鼠标按住移动的时候,即拖拽状态下,绘制线条,代码如下:

// 鼠标移动事件处理函数
void wxFontSelectorCtrl::OnMotion(wxMouseEvent& event)
{
	// 如果为拖拽状态
	if (event.Dragging())
	{
		wxClientDC dc(this);
		dc.SetPen(wxPen(*wxYELLOW, 1));
		dc.DrawPoint(event.GetPosition());
		dc.SetPen(wxNullPen);
	}
}
客户区的绘图用的最多的就是背景绘制,这是与前景绘制EVT_PAINT相对应的,只有需要重绘时才发生,如下:

	EVT_ERASE_BACKGROUND(wxFontSelectorCtrl::OnErase)

// 背景擦除事件处理函数
void wxFontSelectorCtrl::OnErase(wxEraseEvent& event) {
	// 获取一个设备DC
	wxClientDC * clientDC = NULL;
	if(!event.GetDC()) clientDC = new wxClientDC(this);
	wxDC * dc = clientDC ? clientDC : event.GetDC();
	// 绘制黄色背景
	dc->SetBrush(wxBrush(wxColour(255,255,0)));
	wxSize sz = GetClientSize();
	dc->DrawRectangle(wxRect(0,0,sz.x,sz.y));
	dc->SetBrush(wxNullBrush);
	// 清除可能创建的clientDC
	if (clientDC) wxDELETE(clientDC);
}
wxPaintDC

重绘前景的设备DC,只有当需要重绘时,才会发生重绘事件。什么叫需要重绘时?

手动发出一个重绘事件:Reflash与ReflashRect这两个函数,如果没有立即重绘,可以强制调用Update函数。

被动发出一个重绘事件:被别人挡住后,重新出现,或最小化后再重新出现,都会发生重绘事件。

	EVT_PAINT(wxFontSelectorCtrl::OnPaint)
// 前景事件处理函数
void wxFontSelectorCtrl::OnPaint(wxPaintEvent& event)
{
	wxPaintDC dc(this);
	dc.SetPen(*wxBLACK_PEN);
	dc.SetBrush(*wxRED_BRUSH);

	// 判断这个区域是否需要重绘
	wxRect rectToDraw(0,0,100,100);
	if (IsExposed(rectToDraw)) {
		dc.DrawEllipse(wxPoint(0, 0), wxSize(50, 50));
	}
	dc.SetBrush(wxNullBrush);
	dc.SetPen(wxNullPen);
}
防止重绘事件闪烁,可以让擦除背景的函数为空,将前景与背景的绘制全部统一到前景中来,利用双缓冲绘图来实现。

// 前景事件处理函数
void wxFontSelectorCtrl::OnPaint(wxPaintEvent& event)
{
	wxBufferedPaintDC dc(this);

	PrepareDC(dc);
	// 绘制背景色
	...
	// 绘制前景色
	dc.SetPen(*wxBLACK_PEN);
	dc.SetBrush(*wxRED_BRUSH);

	dc.DrawEllipse(wxPoint(0, 0), wxSize(50, 50));
	dc.SetBrush(wxNullBrush);
	dc.SetPen(wxNullPen);
}
wxMemoryDC

双缓冲绘图就是利用这个DC实现的,我们可以把所有的绘制,先在内存DC上绘制好,然后再输出到我们需要绘制的DC中。

下面我们演示一个利用内存DC绘制一个位图的实现代码:

	wxMemoryDC memDC;
	wxBitmap bitmap(200,200);
	memDC.SelectObject(bitmap);
	memDC.SetBackground(*wxWHITE_BRUSH);
	memDC.Clear();
	memDC.SetPen(*wxRED_PEN);
	memDC.SetBrush(*wxTRANSPARENT_BRUSH);
	memDC.DrawRectangle(wxRect(10,10,100,100));
	memDC.SelectObject(wxNullBitmap);

绘图工具

1、wxColour(wxColour wc(255,0,0)红色),还有第4个参数,是apha通道

系统自带的颜色有:wxBLACK,wxWHITE, wxRED, wxBLUE, wxGREEN, wxCYAN,wxLIGHT_GREY,wxNullColour

wxSystemSettings::GetColour获取系统颜色(wxSYS COLOUR 3DFACE)

2、wxPen(wxPen wp(颜色,宽度,线型))

系统的线型有:wxSOLID,wxTRANSPARENT,wxDOT,wxLONG_DASH,wxSHORT_DASH,wxDOT_DASH

3、wxBrush(wxBrush wb(颜色,画刷类型))

画刷类型:wxSOLID,wxTRANSPARENT,wxBDIAGONAL_HATCH,wxCROSSDIAG_HATCH,wxSTIPPLE

系统画刷:wxGREEN BRUSH, wxWHITE BRUSH, wxBLACK BRUSH, wxGREY BRUSH,wxMEDIUM GREY BRUSH, wxLIGHT GREY BRUSH,wxtrANSPARENT BRUSH,wxNullBrush

4、wxFont(wxFont font(16, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD, true,wxT("Consolas"), wxFONTENCODING_ISO8859_1);)

也可以获取字体:wxFont* font = wxTheFontList->FindOrCreateFont(12, wxSWISS,wxNORMAL, wxNORMAL);

5、wxPalette(调色板,估计用的很少)

绘图函数

1、常用的函数有:

  • Blit,拷贝此设备一部分到另一个设备
  • Clear,刷新背景
  • SetClippingRegion和DestroyClippingRegion:设置和释放区域
  • DrawBitmap和DrawIcon:在某个位置画一个位图或者图标
  • DrawCircle和DrawEllipse:画一个园和椭圆
  • DrawLine和DrawLines:画线
  • DrawPoint:画一个点像素
  • DrawPolygon和DrawPolyPolygon:画多边形
  • DrawRectangle和DrawRoundedRectangle:画矩形,或圆角矩形
  • DrawText和DrawRotatedText:绘制文本,旋转文本
  • DrawSpline:画一条平滑的曲线
  • FloodFill:使用画刷填充
  • SetBackground:设备背景画刷
  • SetBackgroundMode:设置背景是实体还是透明wxTRANSPARENT和wxSOLID
  • SetBrush:设置画刷
  • SetPen:设置画笔
  • SetFont:设置字体
  • SetPalette:设置调色板
  • SetTextForeground和SetTextBackground:设置字体前景色和后景色
  • GetPixel:获取像素
  • GetTextExtent和GetPartialTextExtents:获取文本的大小
  • GetSize和GetSizeMM:获取设备的尺寸
  • DeviceToLogicalX:设备坐标转化到逻辑坐标
  • LogicalToDeviceX:逻辑坐标转化到设备坐标
  • SetMapMode:逻辑坐标到设备坐标的映射模式
  • SetAxisOrientation:设备x,y方向
  • SetDeviceOrigin:设置坐标原点
  • SetUserScale:设置缩放指

前景与背景

关于这个问题,wxWidgets框架中比较难以理解,也不同于MFC,因为它是多平台兼容的。

背景的擦除,默认情况下是会调用最近一次的SetBackgroundColour传入的颜色参数来擦除背景。

这个最近一次,概念比较模糊,资料比较欠缺,我的理解是当前控件的直系亲属,比如它的父窗口,或者父窗口的父窗口,设置过Colour,那么默认就以这个Colour为准。

这个擦除的动作,其实和wxDC的函数Clear是一样的,都是调用当前的背景画刷来擦除背景。

也就是说,默认情况下,背景的擦除是调用的当前的背景画刷,而当前的背景画刷默认情况下是直系亲属的默认背景画刷。

如果你没有重写EVT_ERASE_BACKGROUND这个事件,那么可以在EVT_PAINT中如下擦除背景:

	wxPaintDC dc(this);
	dc.SetBackground(wxBrush(wxColour(200, 100, 10)));
	dc.Clear();
效果是一样的,还可以把wxPaintDC换成wxBufferedPaintDC,解决闪烁的问题。

如果主窗口的背景是一张图片,而子控件想要达到透明的效果,是不能通过调用获取主窗口的DC,来平铺当前控件的背景的,这是框架的机制决定的。

要达到这个目标,子窗口必须获取那张图片,然后计算当前控件所占的位置大小,来裁剪那张图片作为背景的位图画刷,擦除背景。


自绘标题栏

使用绘图相关知识,来自绘一个标题栏

wxWidgets教程(4)- 绘图与自绘标题栏_第1张图片

1、从wxPanel继承一个子类,当做标题栏控件

class wxCaptionPanel :public wxPanel
2、定义构造函数与初始化的一些代码

	wxCaptionPanel() { Init(); }
	wxCaptionPanel(wxWindow *parent,
		wxWindowID winid = wxID_ANY,
		const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize,
		long style = wxTAB_TRAVERSAL | wxNO_BORDER,
		const wxString& name = wxPanelNameStr)
	{
		Init();
		Create(parent, winid, pos, size, style, name);
	}

	bool Create(wxWindow *parent,
		wxWindowID winid = wxID_ANY,
		const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize,
		long style = wxTAB_TRAVERSAL | wxNO_BORDER,
		const wxString& name = wxPanelNameStr);

	void Init();
3、定义一些控件的属性,比如标题栏的图标,文字,以及右侧的最小化与关闭按钮的图片和状态,等等

	wxString m_title;
	// 标题栏的区域
	wxRect m_caption_rect;
	// 窗口图标
	wxIcon m_icon;
	// 按钮的图片
	std::vector m_skin_bmps;
	std::vector m_min_bmps;
	std::vector m_close_bmps;
	// 按钮的区域
	wxRect m_skin_rect;
	wxRect m_min_rect;
	wxRect m_close_rect;
	// 按钮的状态
	int m_skin_status;
	int m_min_status;
	int m_close_status;
4、标题栏的关闭与最小化按钮的三种状态

// 按钮的三种状态
enum BTN_STATUS
{
	BTN_NORMAL = 0,BTN_HOVER,BTN_CLICK
};
5、相关的事件处理函数

BEGIN_EVENT_TABLE(wxCaptionPanel, wxPanel)
	EVT_PAINT(wxCaptionPanel::OnPaint)
	EVT_LEFT_DOWN(wxCaptionPanel::OnLeftDown)
	EVT_LEFT_UP(wxCaptionPanel::OnLeftUp)
	EVT_MOTION(wxCaptionPanel::OnMotion)
	EVT_LEAVE_WINDOW(wxCaptionPanel::OnLeave)
	EVT_MOUSE_CAPTURE_LOST(wxCaptionPanel::OnMouseLost)
END_EVENT_TABLE()
6、OnPaint绘图函数

void wxCaptionPanel::OnPaint(wxPaintEvent& event)
{
	wxBufferedPaintDC dc(this);
	dc.Clear();
	// 绘制标题图标
	dc.DrawIcon(m_icon, wxPoint(0,0));
	// 绘制标题文字
	dc.SetTextForeground(wxColour(50, 50, 50));
	dc.DrawText(m_title, wxPoint(40, 10));
	// 绘制按钮
	dc.DrawBitmap(m_skin_bmps[m_skin_status], wxPoint(m_skin_rect.x, m_skin_rect.y), true);
	dc.DrawBitmap(m_min_bmps[m_min_status], wxPoint(m_min_rect.x, m_min_rect.y), true);
	dc.DrawBitmap(m_close_bmps[m_close_status], wxPoint(m_close_rect.x, m_close_rect.y), true);
}


完整的自绘标题栏代码如下:

#pragma once
#include 
#include 
#include "MyApp.h"
#include "MyFrame.h"
#include 

// 按钮的三种状态
enum BTN_STATUS
{
	BTN_NORMAL = 0,BTN_HOVER,BTN_CLICK
};

class wxCaptionPanel :public wxPanel
{
	DECLARE_EVENT_TABLE()
public:
	wxCaptionPanel() { Init(); }
	wxCaptionPanel(wxWindow *parent,
		wxWindowID winid = wxID_ANY,
		const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize,
		long style = wxTAB_TRAVERSAL | wxNO_BORDER,
		const wxString& name = wxPanelNameStr)
	{
		Init();
		Create(parent, winid, pos, size, style, name);
	}

	bool Create(wxWindow *parent,
		wxWindowID winid = wxID_ANY,
		const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize,
		long style = wxTAB_TRAVERSAL | wxNO_BORDER,
		const wxString& name = wxPanelNameStr);

	void Init();
	void SetTitle(const wxString & title) { m_title = title; Refresh(); };

	// 事件处理
	void OnPaint(wxPaintEvent& event);
	void OnMotion(wxMouseEvent& event);
	void OnLeave(wxMouseEvent& event);
	void OnMouseLost(wxMouseCaptureLostEvent& event) {};
	void OnLeftDown(wxMouseEvent& event);
	void OnLeftUp(wxMouseEvent& event);
	void RefreshBtn();
private:
	wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxCaptionPanel);
	MyFrame * m_pFrame;
	wxSize m_offset;
	wxString m_title;
	// 标题栏的区域
	wxRect m_caption_rect;
	// 窗口图标
	wxIcon m_icon;
	// 按钮的图片
	std::vector m_skin_bmps;
	std::vector m_min_bmps;
	std::vector m_close_bmps;
	// 按钮的区域
	wxRect m_skin_rect;
	wxRect m_min_rect;
	wxRect m_close_rect;
	// 按钮的状态
	int m_skin_status;
	int m_min_status;
	int m_close_status;
};


源文件cpp

#include "stdafx.h"
#include "wxCaptionPanel.h"

IMPLEMENT_DYNAMIC_CLASS(wxCaptionPanel, wxPanel)

BEGIN_EVENT_TABLE(wxCaptionPanel, wxPanel)
	EVT_PAINT(wxCaptionPanel::OnPaint)
	EVT_LEFT_DOWN(wxCaptionPanel::OnLeftDown)
	EVT_LEFT_UP(wxCaptionPanel::OnLeftUp)
	EVT_MOTION(wxCaptionPanel::OnMotion)
	EVT_LEAVE_WINDOW(wxCaptionPanel::OnLeave)
	EVT_MOUSE_CAPTURE_LOST(wxCaptionPanel::OnMouseLost)
END_EVENT_TABLE()

bool wxCaptionPanel::Create(wxWindow *parent,
	wxWindowID winid,
	const wxPoint& pos,
	const wxSize& size,
	long style,
	const wxString& name)
{
	if (!wxPanel::Create(parent, winid, pos, size, style, name)) return false;
	SetBackgroundColour(wxColour(0, 206, 209));
	return true;
}
// 初始化标题栏
void wxCaptionPanel::Init() {
	m_offset = wxSize(-1, -1);
	m_title = wxT("");
	m_pFrame = (MyFrame *)wxTheApp->GetTopWindow();
	m_icon = wxICON(MAIN_ICON);
	// 初始化按钮图片
	m_skin_bmps.emplace_back(wxT("res/btn_Skin_normal.png"), wxBITMAP_TYPE_PNG);
	m_skin_bmps.emplace_back(wxT("res/btn_Skin_highlight.png"), wxBITMAP_TYPE_PNG);
	m_skin_bmps.emplace_back(wxT("res/btn_Skin_down.png"), wxBITMAP_TYPE_PNG);
	m_min_bmps.emplace_back(wxT("res/btn_mini_normal.png"), wxBITMAP_TYPE_PNG);
	m_min_bmps.emplace_back(wxT("res/btn_mini_highlight.png"), wxBITMAP_TYPE_PNG);
	m_min_bmps.emplace_back(wxT("res/btn_mini_down.png"), wxBITMAP_TYPE_PNG);
	m_close_bmps.emplace_back(wxT("res/btn_close_normal.png"), wxBITMAP_TYPE_PNG);
	m_close_bmps.emplace_back(wxT("res/btn_close_highlight.png"), wxBITMAP_TYPE_PNG);
	m_close_bmps.emplace_back(wxT("res/btn_close_down.png"), wxBITMAP_TYPE_PNG);
	// 初始化标题栏与按钮的位置
	wxSize szSkin = m_skin_bmps[0].GetSize();
	wxSize szMin = m_min_bmps[0].GetSize();
	wxSize szClose = m_close_bmps[0].GetSize();
	int width = m_pFrame->GetClientSize().GetWidth();
	int height = m_pFrame->GetClientSize().GetHeight();
	m_caption_rect = wxRect(0, 0, width - szSkin.x - szMin.x - szClose.x, height);
	m_skin_rect = wxRect(width - szSkin.x - szMin.x - szClose.x, 0, szSkin.x, szSkin.y);
	m_min_rect = wxRect(width - szMin.x - szClose.x, 0, szMin.x, szMin.y);
	m_close_rect = wxRect(width - szClose.x, 0, szClose.x, szClose.y);
}

void wxCaptionPanel::OnPaint(wxPaintEvent& event)
{
	wxBufferedPaintDC dc(this);
	dc.Clear();
	// 绘制标题图标
	dc.DrawIcon(m_icon, wxPoint(0,0));
	// 绘制标题文字
	dc.SetTextForeground(wxColour(50, 50, 50));
	dc.DrawText(m_title, wxPoint(40, 10));
	// 绘制按钮
	dc.DrawBitmap(m_skin_bmps[m_skin_status], wxPoint(m_skin_rect.x, m_skin_rect.y), true);
	dc.DrawBitmap(m_min_bmps[m_min_status], wxPoint(m_min_rect.x, m_min_rect.y), true);
	dc.DrawBitmap(m_close_bmps[m_close_status], wxPoint(m_close_rect.x, m_close_rect.y), true);
}

// 鼠标移动事件
void wxCaptionPanel::OnMotion(wxMouseEvent& event)
{
	wxPoint pt = event.GetPosition();
	bool isDown = event.LeftIsDown();
	// 拖动时按下,并且在标题栏范围内
	if (isDown && event.Dragging() && m_caption_rect.Contains(pt))
	{
		wxPoint mouse_pos = ClientToScreen(pt);
		m_pFrame->Move(mouse_pos - m_offset);
		return;
	}
	m_skin_status = m_skin_rect.Contains(pt) ? (isDown ? BTN_CLICK : BTN_HOVER) : BTN_NORMAL;
	m_min_status = m_min_rect.Contains(pt) ? (isDown ? BTN_CLICK : BTN_HOVER) : BTN_NORMAL;
	m_close_status = m_close_rect.Contains(pt) ? (isDown ? BTN_CLICK : BTN_HOVER) : BTN_NORMAL;

	RefreshBtn();
}

// 鼠标离开标题栏事件
void wxCaptionPanel::OnLeave(wxMouseEvent& event) {
	m_skin_status = BTN_NORMAL;
	m_min_status = BTN_NORMAL;
	m_close_status = BTN_NORMAL;
	RefreshBtn();
}
// 鼠标按下事件
void wxCaptionPanel::OnLeftDown(wxMouseEvent& event)
{
	CaptureMouse();
	// 当前鼠标的屏幕坐标
	wxPoint pt = event.GetPosition();
	wxPoint mouse_pos = ClientToScreen(pt);
	// 当前主窗口的屏幕坐标
	wxPoint wnd_pos = m_pFrame->GetPosition();
	// 计算鼠标的坐标点到窗口左上角的偏移量
	m_offset.x = mouse_pos.x - wnd_pos.x;
	m_offset.y = mouse_pos.y - wnd_pos.y;

	if (m_skin_rect.Contains(pt)) {
		m_skin_status = BTN_CLICK;
	}
	else if (m_min_rect.Contains(pt)) {
		m_min_status = BTN_CLICK;
	}
	else if (m_close_rect.Contains(pt)) {
		m_close_status = BTN_CLICK;
	}
	RefreshBtn();
}
// 鼠标抬起事件
void wxCaptionPanel::OnLeftUp(wxMouseEvent& event)
{
	if (HasCapture()) ReleaseCapture();

	wxPoint pt = event.GetPosition();
	if (m_skin_rect.Contains(pt)) {
		m_skin_status = BTN_NORMAL;
		srand((unsigned)time(0));
		SetBackgroundColour(wxColour(rand() % 255, rand() % 255, rand() % 255));
		Refresh();
	}else if (m_min_rect.Contains(pt)) {
		m_min_status = BTN_NORMAL; 
		m_pFrame->Iconize();
	}else if (m_close_rect.Contains(pt)) {
		m_close_status = BTN_NORMAL; 
		m_pFrame->Close();
	}
}
// 刷新按钮区域,状态没有变动的,就不刷新,防止闪烁
void wxCaptionPanel::RefreshBtn() {
	static int skin_old_status = BTN_NORMAL;
	static int min_old_status = BTN_NORMAL;
	static int close_old_status = BTN_NORMAL;
	if (skin_old_status != m_skin_status) {
		skin_old_status = m_skin_status;
		RefreshRect(m_skin_rect);
	}
	if (min_old_status != m_min_status) {
		min_old_status = m_min_status;
		RefreshRect(m_min_rect);
	}
	if (close_old_status != m_close_status) {
		close_old_status = m_close_status;
		RefreshRect(m_close_rect);
	}
}




自绘标题栏--项目下载地址: http://download.csdn.net/download/wyansai/10155278










你可能感兴趣的:(wxWidgets)