10、wxWidgets 繪畫wxClientDC wxPaintDC wxMemoryDC


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_PENwxGREEN_PENwxBLUE_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,來平鋪當前控件的背景的,這是框架的機制決定的。

要達到這個目標,子窗口必須獲取那張圖片,然后計算當前控件所占的位置大小,來裁剪那張圖片作為背景的位圖畫刷,擦除背景。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM