wxWidgets 中的设备上下文
最后修改于 2023 年 10 月 18 日
GDI (图形设备接口
) 是一个用于处理图形的接口。它用于与图形设备(如显示器、打印机或文件)进行交互。GDI 允许程序员在屏幕或打印机上显示数据,而无需关心特定设备的细节。GDI 将程序员与硬件隔离。

从程序员的角度来看,GDI 是一组用于处理图形的类和方法。GDI 由 2D 矢量图形、字体和图像组成。
要开始绘制图形,我们必须创建一个设备上下文 (DC) 对象。在 wxWidgets 中,设备上下文被称为 wxDC
。文档将 wxDC
定义为可以绘制图形和文本的设备上下文。它以通用方式表示许多设备。同一段代码可以写入不同类型的设备。无论是屏幕还是打印机。wxDC
并非旨在直接使用。相反,程序员应该选择其中一个派生类。每个派生类都旨在在特定条件下使用。
以下类是 wxDC
的派生类
wxBufferedDC
wxBufferedPaintDC
wxPostScriptDC
wxMemoryDC
wxPrinterDC
wxScreenDC
wxClientDC
wxPaintDC
wxWindowDC
wxScreenDC
用于在屏幕上的任何位置绘制。如果我们要绘制整个窗口(仅限 Windows),则使用 wxWindowDC
。这包括窗口装饰。wxClientDC
用于在窗口的客户区上绘制。客户区是窗口的区域,没有其装饰(标题和边框)。wxPaintDC
也用于在客户区上绘制。但是 wxPaintDC
和 wxClientDC
之间有一个区别。wxPaintDC
应该仅从 wxPaintEvent 使用。wxClientDC
不应从 wxPaintEvent
使用。wxMemoryDC
用于在位图上绘制图形。wxPostScriptDC
用于在任何平台上写入 PostScript 文件。wxPrinterDC
用于访问打印机(仅限 Windows)。
简单线条
我们从绘制一条线开始。
#include <wx/wx.h> class Line : public wxFrame { public: Line(const wxString& title); void OnPaint(wxPaintEvent& event); };
#include "line.h" Line::Line(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(280, 180)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Line::OnPaint)); this->Centre(); } void Line::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); wxCoord x1 = 50, y1 = 60; wxCoord x2 = 190, y2 = 60; dc.DrawLine(x1, y1, x2, y2); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "line.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Line *line = new Line(wxT("Line")); line->Show(true); return true; }
在我们的示例中,我们在窗口的客户区上绘制一条简单的线。如果我们调整窗口大小,它将被重绘。将生成一个 wxPaintEvent
。并且该线将被再次绘制。
void OnPaint(wxPaintEvent& event);
在这里,我们声明一个 OnPaint
事件处理程序函数。
this->Connect(wxEVT_PAINT, wxPaintEventHandler(Line::OnPaint));
我们将一个绘图事件连接到 OnPaint
方法。所有绘图都发生在 OnPaint
事件处理程序内部。
wxPaintDC dc(this);
我们定义一个 wxPaintDC
设备上下文。它是一个设备上下文,用于在 wxPaintEvent
内部的窗口上绘制
wxCoord x1 = 50, y1 = 60; wxCoord x2 = 190, y2 = 60;
我们定义四个坐标。
dc.DrawLine(x1, y1, x2, y2);
我们通过调用 DrawLine
方法绘制一条简单的线。

绘制文本
在窗口上绘制一些文本很容易。
#include <wx/wx.h> class Text : public wxFrame { public: Text(const wxString & title); void OnPaint(wxPaintEvent & event); };
#include "text.h" Text::Text(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 150)) { Connect(wxEVT_PAINT, wxPaintEventHandler(Text::OnPaint)); Centre(); } void Text::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); dc.DrawText(wxT("Лев Николaевич Толстoй"), 40, 60); dc.DrawText(wxT("Анна Каренина"), 70, 80); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "text.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Text *text = new Text(wxT("Text")); text->Show(true); return true; }
在我们的示例中,我们在窗口上绘制文本 Lev Nikolayevich Tolstoy, Anna Karenina,使用俄语字母 azbuka。
dc.DrawText(wxT("Лев Николaевич Толстoй"), 40, 60); dc.DrawText(wxT("Анна Каренина"), 70, 80);
DrawText
方法在窗口上绘制文本。它使用当前的文本字体以及当前的文本前景色和背景色,在指定点绘制文本字符串。由于使用了 wxT
宏,我们可以在代码中直接使用 azbuka。wxT
宏与 _T
宏相同。它包装字符串字面量以供使用,无论是否使用 Unicode。当未启用 Unicode 时,wxT
是一个空宏。当启用 Unicode 时,它会为字符串字面量添加必要的 L,使其成为宽字符字符串常量。

点
最简单的几何对象是一个点。它在窗口上只是一个点。
DrawPoint(int x, int y)
此方法在 x、y 坐标处绘制一个点。
#include <wx/wx.h> class Points : public wxFrame { public: Points(const wxString & title); void OnPaint(wxPaintEvent & event); };
#include "points.h" #include <stdlib.h> #include <time.h> Points::Points(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(280, 180)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Points::OnPaint)); srand(time(NULL)); this->Centre(); } void Points::OnPaint(wxPaintEvent & event) { wxPaintDC dc(this); wxCoord x = 0; wxCoord y = 0; wxSize size = this->GetSize(); for (int i = 0; i<1000; i++) { x = rand() % size.x + 1; y = rand() % size.y + 1; dc.DrawPoint(x, y); } }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "points.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Points *points = new Points(wxT("Points")); points->Show(true); return true; }
单个点可能难以看到。因此我们创建了 1000 个点。每次调整窗口大小时,我们都会在窗口的客户区上绘制 1000 个点。
wxSize size = this->GetSize();
在这里,我们获取窗口的大小。
x = rand() % size.x + 1;
在这里,我们获得一个介于 1 到 size.x
之间的随机数。

画笔
画笔是一个基本的图形对象。它用于绘制线条、曲线以及矩形、椭圆、多边形或其他形状的轮廓。
wxPen(const wxColour& colour, int width = 1, int style = wxSOLID)
wxPen
构造函数有三个参数:颜色、宽度和样式。以下是可能的画笔样式列表
wxSOLID
wxDOT
wxLONG_DASH
wxSHORT_DASH
wxDOT_DASH
wxTRANSPARENT
#include <wx/wx.h> class Pen : public wxFrame { public: Pen(const wxString& title); void OnPaint(wxPaintEvent& event); };
#include "pen.h" Pen::Pen(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(360, 180)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Pen::OnPaint)); this->Centre(); } void Pen::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); wxColour col1, col2; col1.Set(wxT("#0c0c0c")); col2.Set(wxT("#000000")); wxBrush brush(wxColour(255, 255, 255), wxTRANSPARENT); dc.SetBrush(brush); dc.SetPen(wxPen(col1, 1, wxSOLID)); dc.DrawRectangle(10, 15, 90, 60); dc.SetPen(wxPen(col1, 1, wxDOT)); dc.DrawRectangle(130, 15, 90, 60); dc.SetPen(wxPen(col1, 1, wxLONG_DASH)); dc.DrawRectangle(250, 15, 90, 60); dc.SetPen(wxPen(col1, 1, wxSHORT_DASH)); dc.DrawRectangle(10, 105, 90, 60); dc.SetPen(wxPen(col1, 1, wxDOT_DASH)); dc.DrawRectangle(130, 105, 90, 60); dc.SetPen(wxPen(col1, 1, wxTRANSPARENT)); dc.DrawRectangle(250, 105, 90, 60); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "pen.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Pen *pen = new Pen(wxT("Pen")); pen->Show(true); return true; }
在我们的示例中,我们绘制了 6 个具有不同画笔样式的矩形。最后一个是透明的,不可见。
dc.SetPen(wxPen(col1, 1, wxSOLID)); dc.DrawRectangle(10, 15, 90, 60);
在这里,我们为我们的第一个矩形定义一个画笔。我们设置一个颜色为 col1
(#0c0c0c)、宽度为 1 像素、实心的画笔。DrawRectangle
方法绘制矩形。

区域
区域可以组合起来创建更复杂的形状。我们可以使用四个集合操作:Union
、Intersect
、Substract
和 Xor
。以下示例展示了所有四个操作。
#include <wx/wx.h> class Regions : public wxFrame { public: Regions(const wxString & title); void OnPaint(wxPaintEvent & event); };
#include "Regions.h" Regions::Regions(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Regions::OnPaint)); this->Centre(); } void Regions::OnPaint(wxPaintEvent & event) { wxPaintDC dc(this); wxColour gray, white, red, blue; wxColour orange, green, brown; gray.Set(wxT("#d4d4d4")); white.Set(wxT("#ffffff")); red.Set(wxT("#ff0000")); orange.Set(wxT("#fa8e00")); green.Set(wxT("#619e1b")); brown.Set(wxT("#715b33")); blue.Set(wxT("#0d0060")); dc.SetPen(wxPen(gray)); dc.DrawRectangle(20, 20, 50, 50); dc.DrawRectangle(30, 40, 50, 50); dc.SetBrush(wxBrush(white)); dc.DrawRectangle(100, 20, 50, 50); dc.DrawRectangle(110, 40, 50, 50); wxRegion region1(100, 20, 50, 50); wxRegion region2(110, 40, 50, 50); region1.Intersect(region2); wxRect rect1 = region1.GetBox(); dc.SetClippingRegion(region1); dc.SetBrush(wxBrush(red)); dc.DrawRectangle(rect1); dc.DestroyClippingRegion(); dc.SetBrush(wxBrush(white)); dc.DrawRectangle(180, 20, 50, 50); dc.DrawRectangle(190, 40, 50, 50); wxRegion region3(180, 20, 50, 50); wxRegion region4(190, 40, 50, 50); region3.Union(region4); dc.SetClippingRegion(region3); wxRect rect2 = region3.GetBox(); dc.SetBrush(wxBrush(orange)); dc.DrawRectangle(rect2); dc.DestroyClippingRegion(); dc.SetBrush(wxBrush(white)); dc.DrawRectangle(20, 120, 50, 50); dc.DrawRectangle(30, 140, 50, 50); wxRegion region5(20, 120, 50, 50); wxRegion region6(30, 140, 50, 50); region5.Xor(region6); wxRect rect3 = region5.GetBox(); dc.SetClippingRegion(region5); dc.SetBrush(wxBrush(green)); dc.DrawRectangle(rect3); dc.DestroyClippingRegion(); dc.SetBrush(wxBrush(white)); dc.DrawRectangle(100, 120, 50, 50); dc.DrawRectangle(110, 140, 50, 50); wxRegion region7(100, 120, 50, 50); wxRegion region8(110, 140, 50, 50); region7.Subtract(region8); wxRect rect4 = region7.GetBox(); dc.SetClippingRegion(region7); dc.SetBrush(wxBrush(brown)); dc.DrawRectangle(rect4); dc.DestroyClippingRegion(); dc.SetBrush(white); dc.DrawRectangle(180, 120, 50, 50); dc.DrawRectangle(190, 140, 50, 50); wxRegion region9(180, 120, 50, 50); wxRegion region10(190, 140, 50, 50); region10.Subtract(region9); wxRect rect5 = region10.GetBox(); dc.SetClippingRegion(region10); dc.SetBrush(wxBrush(blue)); dc.DrawRectangle(rect5); dc.DestroyClippingRegion(); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "Regions.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Regions *regions = new Regions(wxT("Regions")); regions->Show(true); return true; }

渐变
在计算机图形学中,渐变是从亮到暗或从一种颜色到另一种颜色的平滑混合。在 2D 绘图程序和绘画程序中,渐变用于创建彩色背景和特殊效果,以及模拟光线和阴影。(answers.com)
void GradientFillLinear(const wxRect& rect, const wxColour& initialColour, const wxColour& destColour, wxDirection nDirection = wxEAST)
此方法使用线性渐变填充由矩形指定的区域,从 initialColour
开始,最终淡化到 destColour
。nDirection
参数指定颜色更改的方向,默认值为 wxEAST
。
#include <wx/wx.h> class Gradient : public wxFrame { public: Gradient(const wxString& title); void OnPaint(wxPaintEvent& event); };
#include "gradient.h" Gradient::Gradient(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(220, 260)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Gradient::OnPaint)); this->Centre(); } void Gradient::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); wxColour col1, col2; col1.Set(wxT("#e12223")); col2.Set(wxT("#000000")); dc.GradientFillLinear(wxRect(20, 20, 180, 40), col1, col2, wxNORTH); dc.GradientFillLinear(wxRect(20, 80, 180, 40), col1, col2, wxSOUTH); dc.GradientFillLinear(wxRect(20, 140, 180, 40), col1, col2, wxEAST); dc.GradientFillLinear(wxRect(20, 200, 180, 40), col1, col2, wxWEST); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "gradient.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Gradient *grad = new Gradient(wxT("Gradient")); grad->Show(true); return true; }

形状
形状是更复杂的几何对象。我们在以下示例中绘制各种几何形状。
#include <wx/wx.h> class Shapes : public wxFrame { public: Shapes(const wxString & title); void OnPaint(wxPaintEvent & event); };
#include "shapes.h" Shapes::Shapes(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(350, 300)) { this->Connect(wxEVT_PAINT, wxPaintEventHandler(Shapes::OnPaint)); this->Centre(); } void Shapes::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); wxPoint lines[] = { wxPoint(20, 260), wxPoint(100, 260), wxPoint(20, 210), wxPoint(100, 210) }; wxPoint polygon[] = { wxPoint(130, 140), wxPoint(180, 170), wxPoint(180, 140), wxPoint(220, 110), wxPoint(140, 100) }; wxPoint splines[] = { wxPoint(240, 170), wxPoint(280, 170), wxPoint(285, 110), wxPoint(325, 110) }; dc.DrawEllipse(20, 20, 90, 60); dc.DrawRoundedRectangle(130, 20, 90, 60, 10); dc.DrawArc(240, 40, 340, 40, 290, 20); dc.DrawPolygon(4, polygon); dc.DrawRectangle(20, 120, 80, 50); dc.DrawSpline(4, splines); dc.DrawLines(4, lines); dc.DrawCircle(170, 230, 35); dc.DrawRectangle(250, 200, 60, 60); }
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
#include "main.h" #include "shapes.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Shapes *shapes = new Shapes(wxT("Shapes")); shapes->Show(true); return true; }

在本章中,我们介绍了 wxWidgets 中的 GDI。