wxDC
1、所有的繪圖設備類都是繼承自wxDC。
2、關於坐標系
默認的坐標原點在屏幕左上角,當然這是可以改變的,使用函數SetDeviceOrigin。此函數僅改變當前dc的坐標原點,一般用於打印文稿的時候,設置打印設備的原點。
void SetDeviceOrigin(wxCoord x, wxCoord y)
類型wxCoord的原型是整形int,英文中是坐標的意思。當然坐標系的方向也是可以改變的,使用以下函數:
1 void SetAxisOrientation(bool xLeftRight, bool yBottomUp)
第一個參數:為true時,從左向右,反之。。。第二個參數:為true時,從下向上。主要用途為股票趨勢圖這類和數學關系比較大的場合,這些要求左下角為原點,x軸向右,y軸向上。
3、繪圖設備的大小(邏輯單位--像素與設備單位--毫米)
按照像素單位獲取設備的大小:GetSize
按照毫米單位獲取設備的大小:GetSizeMM
獲取設備每英寸的像素密度ppi:GetPPI
獲取設備每像素占的位寬:GetDepth
更改邏輯單位與設備單位的縮放比例:SetUserScale
1 wxClientDC dc(this); 2 dc.SetMapMode(wxMM_TEXT); 3 dc.SetUserScale(1.0,1.0);
在wxMM_TEXT模式下,縮放比例1.0,1.0,可以將邏輯單位與設備單位等同。
4、區域繪圖
所謂區域繪圖,是指定一個區域,所有超過這個區域的范圍都將被忽略。一般情況下,在某個區域內不停的繪制文字時,會有重影的情況,需要不斷擦除這個區域,重新繪制文字。
1 // 鼠標移動事件響應 2 void wxFontSelectorCtrl::OnMouseEvent(wxMouseEvent &event) 3 { 4 wxClientDC dc(this); 5 // 設置一個矩形區域 6 dc.SetClippingRegion(wxPoint(0,0),wxSize(200,20)); 7 // 清除之前繪制的文字 8 dc.Clear(); 9 // 設置文字的前景色 10 dc.SetTextForeground(wxColour(255,255,0)); 11 // 設置文字的字體 12 dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("宋體"))); 13 wxPoint pt = event.GetPosition(); 14 dc.DrawText(wxString::Format(wxT("當前坐標x-%d y-%d"), pt.x, pt.y),wxPoint(0,0)); 15 dc.SetFont(wxNullFont); 16 // 銷毀之前設置的區域 17 dc.DestroyClippingRegion(); 18 }
上面的繪圖會有閃爍的情況,可以使用雙緩沖繪圖,代碼如下:
1 wxClientDC clientDC(this); 2 // 設置一個矩形區域 3 clientDC.SetClippingRegion(wxPoint(0, 0), wxSize(200, 20)); 4 5 wxBufferedDC dc(&clientDC); 6 ...
完整代碼如下:
main.h
1 #include <wx/wx.h> 2 #include <wx/dcbuffer.h> 3 //定義主窗口類 4 class MyPanel : public wxPanel 5 { 6 public: 7 MyPanel(wxFrame * frame, wxWindowID id); 8 9 void OnMotion(wxMouseEvent & event); 10 }; 11 12 class MyFrame : public wxFrame 13 { 14 public: 15 MyPanel * panel1; 16 MyFrame(const wxString& title); 17 18 }; 19 20 //定義應用程序類 21 class MyApp : public wxApp 22 { 23 public: 24 virtual bool OnInit(); 25 };
main.cpp
1 #include "main.h" 2 3 MyPanel::MyPanel(wxFrame * frame, wxWindowID id) 4 :wxPanel(frame, id) 5 { 6 Connect(wxEVT_MOTION, wxMouseEventHandler(MyPanel::OnMotion)); 7 } 8 9 void MyPanel::OnMotion(wxMouseEvent & event) 10 { 11 wxClientDC clientDc(this); 12 13 clientDc.SetClippingRegion(wxPoint(0, 0), wxSize(200, 20)); 14 15 wxBufferedDC dc(&clientDc); 16 18 dc.Clear(); 19 20 dc.SetTextForeground(wxColor(0, 0, 0)); 21 22 dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("宋體"))); 23 wxPoint pt = event.GetPosition(); 24 dc.DrawText(wxString::Format(wxT("當前坐標x-%d, y-%d"), pt.x, pt.y), wxPoint(0, 0)); 25 dc.SetFont(wxNullFont); 26 27 dc.DestroyClippingRegion(); 33 } 34 35 MyFrame::MyFrame(const wxString& title) 36 :wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(400, 600)) 37 { 38 MyPanel * panel1 = new MyPanel(this, wxID_ANY); 39 40 Centre();//整個窗口在可視窗口中居中 41 } 42 44 //聲明應用程序 45 IMPLEMENT_APP(MyApp) 46 //初始化應用程序 47 bool MyApp::OnInit() 48 { 49 MyFrame *btnapp = new MyFrame(wxT("MyFrame")); 50 btnapp->Show(true); 51 52 return true; 53 }
wxClientDC
客戶區繪圖設備DC,用來在非重繪的事件處理函數中使用,即除了EVT_PAINT和EVT_NC_PAINT事件之外的都可以。
例如,在鼠標按住移動的時候,即拖拽狀態下,繪制線條,代碼如下:
1 // 鼠標移動事件處理函數 2 void wxFontSelectorCtrl::OnMotion(wxMouseEvent& event) 3 { 4 // 如果為拖拽狀態 5 if (event.Dragging()) 6 { 7 wxClientDC dc(this); 8 dc.SetPen(wxPen(*wxYELLOW, 1));//wxPen(*wxYELLOW, 1)定義一個畫筆,顏色為黃色,畫筆寬度為1 9 dc.DrawPoint(event.GetPosition());//在鼠標的位置繪制一個點 10 dc.SetPen(wxNullPen);//清除畫筆 11 } 12 }
客戶區的繪圖用的最多的就是背景繪制,這是與前景繪制EVT_PAINT相對應的,只有需要重繪時才發生,如下:
EVT_ERASE_BACKGROUND(wxFontSelectorCtrl::OnErase)
1 // 背景擦除事件處理函數 2 void wxFontSelectorCtrl::OnErase(wxEraseEvent& event) { 3 // 獲取一個設備DC 4 wxClientDC * clientDC = NULL; 5 if(!event.GetDC()) clientDC = new wxClientDC(this); 6 wxDC * dc = clientDC ? clientDC : event.GetDC(); 7 // 繪制黃色背景 8 dc->SetBrush(wxBrush(wxColour(255,255,0))); 9 wxSize sz = GetClientSize(); 10 dc->DrawRectangle(wxRect(0,0,sz.x,sz.y)); 11 dc->SetBrush(wxNullBrush); 12 // 清除可能創建的clientDC 13 if (clientDC) wxDELETE(clientDC); 14 }
wxPaintDC
重繪前景的設備DC,只有當需要重繪時,才會發生重繪事件。什么叫需要重繪時?手動發出一個重繪事件:Reflash與ReflashRect這兩個函數,如果沒有立即重繪,可以強制調用Update函數。被動發出一個重繪事件:被別人擋住后,重新出現,或最小化后再重新出現,都會發生重繪事件。
EVT_PAINT(wxFontSelectorCtrl::OnPaint)
1 // 前景事件處理函數 2 void wxFontSelectorCtrl::OnPaint(wxPaintEvent& event) 3 { 4 wxPaintDC dc(this); 5 dc.SetPen(*wxBLACK_PEN); 6 dc.SetBrush(*wxRED_BRUSH); 7 8 // 判斷這個區域是否需要重繪 9 wxRect rectToDraw(0,0,100,100);//定義一個矩形區域,矩形的邊長為100 10 if (IsExposed(rectToDraw)) { 11 dc.DrawEllipse(wxPoint(0, 0), wxSize(50, 50));//畫一個原型,圓點位置為(50, 50) 12 } 13 dc.SetBrush(wxNullBrush);//清楚畫刷顏色 14 dc.SetPen(wxNullPen);//請出去畫筆顏色 15 }
防止重繪事件閃爍,可以讓擦除背景的函數為空,將前景與背景的繪制全部統一到前景中來,利用雙緩沖繪圖來實現。
1 // 前景事件處理函數 2 void wxFontSelectorCtrl::OnPaint(wxPaintEvent& event) 3 { 4 wxBufferedPaintDC dc(this); 5 6 PrepareDC(dc); 7 // 繪制背景色 8 ... 9 // 繪制前景色 10 dc.SetPen(*wxBLACK_PEN); 11 dc.SetBrush(*wxRED_BRUSH); 12 13 dc.DrawEllipse(wxPoint(0, 0), wxSize(50, 50)); 14 dc.SetBrush(wxNullBrush); 15 dc.SetPen(wxNullPen); 16 }
wxMemoryDC
雙緩沖繪圖就是利用這個DC實現的,我們可以把所有的繪制,先在內存DC上繪制好,然后再輸出到我們需要繪制的DC中。下面我們演示一個利用內存DC繪制一個位圖的實現代碼:
1 wxMemoryDC memDC;//創建一個內存設備上下文 2 wxBitmap bitmap(200,200);//使用當前的顏色深度創建一個200*200的位圖
//將創建的圖片和內存設備上下文關聯 3 memDC.SelectObject(bitmap);//SelectObject()函數的作用是把一個對象(位圖、畫筆、畫刷等)選入指定的設備描述表,新的對象代替同一類型的老對象 4 memDC.SetBackground(*wxWHITE_BRUSH); 5 memDC.Clear(); 6 memDC.SetPen(*wxRED_PEN); 7 memDC.SetBrush(*wxTRANSPARENT_BRUSH); 8 memDC.DrawRectangle(wxRect(10,10,100,100)); 9 memDC.SelectObject(wxNullBitmap);//解除設備上下文和位圖的關聯
繪圖工具
1 1、wxColour(wxColour wc(255,0,0)紅色),還有第4個參數,是apha通道 2 //系統自帶的顏色有:wxBLACK,wxWHITE, wxRED, wxBLUE, wxGREEN, wxCYAN,wxLIGHT_GREY,wxNullColour,wxSystemSettings::GetColour獲取系統顏色(wxSYS COLOUR 3DFACE) 3 4 2、wxPen(wxPen wp(顏色,寬度,線型)) 5 //系統的線型有:wxSOLID,wxTRANSPARENT,wxDOT,wxLONG_DASH,wxSHORT_DASH,wxDOT_DASH 6 3、wxBrush(wxBrush wb(顏色,畫刷類型)) 7 //畫刷類型:wxSOLID,wxTRANSPARENT,wxBDIAGONAL_HATCH,wxCROSSDIAG_HATCH,wxSTIPPLE 8 //系統畫刷:wxGREEN BRUSH, wxWHITE BRUSH, wxBLACK BRUSH, wxGREY BRUSH,wxMEDIUM GREY BRUSH, wxLIGHT GREY BRUSH,wxtrANSPARENT BRUSH,wxNullBrush 9 4、wxFont(wxFont font(16, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD, true,wxT("Consolas"), wxFONTENCODING_ISO8859_1);) 10 //也可以獲取字體:wxFont* font = wxTheFontList->FindOrCreateFont(12, wxSWISS,wxNORMAL, wxNORMAL); 11 5、wxPalette(調色板,估計用的很少)
繪圖原理
繪制圖形
計算機繪制圖像與人類畫畫有很多相似的地方。也需要畫板、繪圖工具(筆、刷子)。面板Panel就是我們的畫板,繪圖工具在程序中被稱為設備上下文(Device Context,簡稱DC)。DC提供了繪制各種圖像的方法。
在wxWidgets常用wxPaintDC
繪制圖像。它可以繪制各種圖像。例如:直線、矩形、

說明
wxPoint
表示一個二維坐標的點,構造函數是wxPoint(int x,int y)
。wxCoord
表示一個數字值,原型是整形int
。wxSize
表示寬高,構造函數是wxPoint(int width,int height)
。

注意:如果兩個圖形存在重合的部分,后面繪制的會覆蓋前面繪制的。
設置繪制
上面的圖形都是黑線白底,線的寬度是1個像素,可以通過下面的方式修改。
說明
顏色設置有兩種方式
1、使用內置的宏定義。例如:wxRED_PEN
,wxGREEN_PEN
,wxBLUE_PEN
等。
2、 使用wxColour
定義顏色,使用RGB模型。
注意
SetPen()
不能改變文字的顏色,需要下面的使用SetFont()
。
1 // 畫文字 2 dc.SetTextForeground(wxColour(255,0 , 255));// 設置字體顏色 3 4 dc.SetFont(wxFontInfo(12).Bold(2).FaceName(wxT("MS yahei"))); // 設置字體大小,粗細,字體 5 6 dc.DrawText(wxT("測試文字"), wxPoint(200, 160));
設置字體顏色要使用SetTextForeground()
方法。
繪制圖片
繪制圖片需要用到如下兩個函數:
【示例】
1 // 繪制圖片 2 wxInitAllImageHandlers(); 3 dc.DrawBitmap(wxBitmap(wxT("logo.png"),wxBITMAP_TYPE_ANY),wxPoint(30,30));
鼠標事件
界面上的圖像有一部分使用代碼生成,也有一部分使用鼠標創建。添加鼠標事件方式與繪制事件一樣。需要新建鼠標處理函數並且綁定到事件中。但是鼠標事件種類要比繪圖事件多,常用的有如下幾個。
【示例】
1 #include <wx/wx.h> 2 class Move : public wxFrame 3 { 4 public: Move(const wxString& title): wxFrame(NULL, wxID_ANY, title) 5 { 6 wxPanel* panel = new wxPanel(this, -1); 7 st1 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 10)); 8 st2 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 30)); 9 panel->Bind(wxEVT_MOTION,&Move::OnMove,this); 10 } 11 void OnMove(wxMouseEvent & event) 12 { 13 wxPoint size = event.GetPosition(); 14 st1->SetLabel(wxString::Format(wxT("x: %d"), size.x )); 15 st2->SetLabel(wxString::Format(wxT("y: %d"), size.y )); 16 event.Skip(); Update(); 17 } 18 private: 19 wxStaticText *st1; 20 wxStaticText *st2; 21 }; 22 class MyApp : public wxApp 23 { 24 public: virtual bool OnInit() 25 { 26 Move *move = new Move(wxT("Move event")); 27 move->Show(true); 28 return true; 29 } 30 }; 31 IMPLEMENT_APP(MyApp)
前景與背景
關於這個問題,wxWidgets框架中比較難以理解,也不同於MFC,因為它是多平台兼容的。背景的擦除,默認情況下是會調用最近一次的SetBackgroundColour傳入的顏色參數來擦除背景。這個最近一次,概念比較模糊,資料比較欠缺,我的理解是當前控件的直系親屬,比如它的父窗口,或者父窗口的父窗口,設置過Colour,那么默認就以這個Colour為准。這個擦除的動作,其實和wxDC的函數Clear是一樣的,都是調用當前的背景畫刷來擦除背景。也就是說,默認情況下,背景的擦除是調用的當前的背景畫刷,而當前的背景畫刷默認情況下是直系親屬的默認背景畫刷。
如果你沒有重寫EVT_ERASE_BACKGROUND這個事件,那么可以在EVT_PAINT中如下擦除背景:
1 wxPaintDC dc(this); 2 dc.SetBackground(wxBrush(wxColour(200, 100, 10))); 3 dc.Clear();
效果是一樣的,還可以把wxPaintDC換成wxBufferedPaintDC,解決閃爍的問題。
如果主窗口的背景是一張圖片,而子控件想要達到透明的效果,是不能通過調用獲取主窗口的DC,來平鋪當前控件的背景的,這是框架的機制決定的。
要達到這個目標,子窗口必須獲取那張圖片,然后計算當前控件所占的位置大小,來裁剪那張圖片作為背景的位圖畫刷,擦除背景。