• 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,来平铺当前控件的背景的,这是框架的机制决定的。

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

  • 相关阅读:
    锤子科技官网:问题整理及注意事项
    springboot中文文档
    Spring Framework 开发参考手册中文(在线HTML)
    .is() 全选复选的判断
    c:forEach用法
    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
    火狐浏览器下载文件保存文件名的乱码问题
    多线程安全的解决方法
    MySQL的concat以及group_concat的用法
    mysql 将时间转换成时间戳
  • 原文地址:https://www.cnblogs.com/Long-w/p/9620147.html
Copyright © 2020-2023  润新知