wxpython入门第八步(画图)

2023-10-20 16:10

本文主要是介绍wxpython入门第八步(画图),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

wxPython graphics

GDI(Graphics Device Interface,图形设备接口)是一个与图形工作的接口,用于与显示器、打印机或文件等图形设备交互。它用于与显示器、打印机或文件等图形设备进行交互。GDI允许程序员在屏幕或打印机上显示数据,而不必关注特定设备的细节。GDI将程序员与硬件隔离开来。

从程序员的角度来看,GDI是一组用于处理图形的类和方法。GDI由二维矢量图形、字体和图像组成。

The GDI

要开始绘制图形,我们必须创建一个设备上下文DC)对象。在 wxPython 中,设备上下文被称为 wx.DC。文档中把wx.DC定义为一个可以绘制图形和文本的设备上下文。它以一种通用的方式表示设备的数量。同一段代码可以写到不同类型的设备上。无论是屏幕还是打印机。wx.DC不是用来直接使用的。相反,程序员应该选择一个派生类。每个派生类都是为了在特定条件下使用。

派生 wx.DC 类

  • wxBufferedDC
  • wxBufferedPaintDC
  • wxPostScriptDC
  • wxMemoryDC
  • wxPrinterDC
  • wxScreenDC
  • wxClientDC
  • wxPaintDC
  • wxWindowDC

wx.ScreenDC用于在屏幕上的任何地方绘画。wx.WindowDC用于在整个窗口上作画(仅限Windows)。这包括窗口装饰。wx.ClientDC用于在窗口的客户端区域绘制。客户端区域是指没有装饰的窗口区域(标题和边框)。wx.PaintDC也用于在客户端区域进行绘制。但是wx.PaintDCwx.ClientDC之间有一个区别。wx.PaintDC只能在wx.PaintEvent中使用。wx.ClientDC不应该在wx.PaintEvent中使用。wx.MemoryDC用于在位图上绘制图形。wx.PostScriptDC用于在任何平台上写入PostScript文件。wx.PrinterDC用于访问打印机(仅限Windows)。

画线

我们的第一个例子将在窗口的客户端区域画一条简单的线。

DrawLine(self, x1, y1, x2, y2)

这种方法是从第一点到第二点画一条线;不包括第二点。

#draw_line.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):wx.CallLater(2000, self.DrawLine)self.SetTitle("Line")self.Centre()def DrawLine(self):dc = wx.ClientDC(self)dc.DrawLine(50, 60, 190, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()

两秒过后,我们在框架窗口上画一条线。

wx.CallLater(2000, self.DrawLine)

我们在窗口创建后调用DrawLine()方法。我们这样做是因为,当窗口创建时,它就被绘制了。因此,我们所有的绘图都将丢失。我们可以在窗口被创建后再开始绘制。这就是为什么我们调用wx.CallLater()方法的原因。

def DrawLine(self):dc = wx.ClientDC(self)dc.DrawLine(50, 60, 190, 60)

我们创建一个wx.ClientDC设备上下文。唯一的参数是我们要绘制的窗口。在我们的例子中,它是self,是对wx.Frame widget的引用。我们调用设备上下文的DrawLine()方法。这个调用实际上是在我们的窗口上画一条线。

理解以下行为是非常重要的。如果我们调整窗口的大小,这条线就会消失。为什么会出现这种情况?如果调整窗口的大小,每个窗口都会被重新绘制。如果它被最大化,它也会被重新绘制。如果我们用另一个窗口覆盖窗口,然后再揭开,窗口也会被重新绘制。窗口被绘制到默认状态,我们的线就会丢失。每次调整窗口的大小时,我们都必须画线。解决方法是wx.PaintEvent。每次窗口被重新绘制时,这个事件都会被触发。我们将在一个与paint事件挂钩的方法中画线。

下面的例子展示了如何完成。

#draw_line2.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Line")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.DrawLine(50, 60, 190, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()

我们画的是同一条线。这一次是对paint事件的反应。

self.Bind(wx.EVT_PAINT, self.OnPaint)

这里我们将OnPaint方法绑定到wx.PaintEvent事件。这意味着每次我们的窗口被重新绘制时,我们调用OnPaint()方法。现在,如果我们调整窗口的大小(覆盖它,最大化它),这条线不会消失。

dc = wx.PaintDC(self)

请注意,这次我们使用了wx.PaintDC

image-20201101102113582

计算机图形

有两种不同的计算机图形。矢量和光栅图形。栅格图形以像素的集合来表示图像。矢量图形是使用几何基元,如点、线、曲线或多边形来表示图像。这些基元是使用数学公式创建的。

两种类型的计算机图形都有优点和缺点。与光栅相比,矢量图形的优点是:

  • 尺寸较小
  • 可无限放大
  • 移动、缩放、填充或旋转不会降低图像的质量。

以下是部分图形的列表。

  • points
  • lines
  • polylines
  • polygons
  • circles
  • ellipses
  • splines

属性

包含几个属性,如Brush、Pen或Font。wx.Brush是一个用于填充区域的绘画工具。它用于绘制形状的背景。它有一个颜色和一个样式。wx.Pen用于绘制形状的轮廓。它有一个颜色,一个宽度和一个样式。wx.Font是一个决定文本外观的对象。

基本对象

在下面的行文中,我们将介绍几个基本的对象:颜色、笔刷、笔、连接、帽和渐变。

颜色

颜色是代表红、绿、蓝(RGB)强度值组合的对象。有效的RGB值的范围是0到255。有三种方法可以设置颜色。我们可以创建一个wx.Colour对象,使用一个预定义的颜色名称或使用十六进制值字符串。wx.Colour(0,0,255)'BLUE''#0000FF'。这三种符号产生相同的颜色。

我们有一个预定义的颜色名称列表,我们可以在程序中使用。

AQUAMARINEBLACKBLUEBLUE VIOLETBROWN
CADET BLUECORALCORNFLOWER BLUECYANDARK GREY
DARK GREENDARK OLIVE GREENDARK ORCHIDDARK SLATE BLUEDARK SLATE GREY
DARK TURQUOISEDIM GREYFIREBRICKFOREST GREENGOLD
GOLDENRODGREYGREENGREEN YELLOWINDIAN RED
KHAKILIGHT BLUELIGHT GREYLIGHT STEEL BLUELIME GREEN
MAGENTAMAROONMEDIUM AQUAMARINEMEDIUM BLUEMEDIUM FOREST GREEN
MEDIUM GOLDENRODMEDIUM ORCHIDMEDIUM SEA GREENMEDIUM SLATE BLUEMEDIUM SPRING GREEN
MEDIUM TURQUOISEMEDIUM VIOLET REDMIDNIGHT BLUENAVYORANGE
ORANGE REDORCHIDPALE GREENPINKPLUM
PURPLEREDSALMONSEA GREENSIENNA
SKY BLUESLATE BLUESPRING GREENSTEEL BLUETAN
THISTLETURQUOISEVIOLETVIOLET REDWHEAT
WHITEYELLOWYELLOW GREEN

下面的例子使用了一些颜色值。

#colours.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Colours")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('#d4d4d4'))dc.SetBrush(wx.Brush('#c56c00'))dc.DrawRectangle(10, 15, 90, 60)dc.SetBrush(wx.Brush('#1ac500'))dc.DrawRectangle(130, 15, 90, 60)dc.SetBrush(wx.Brush('#539e47'))dc.DrawRectangle(250, 15, 90, 60)dc.SetBrush(wx.Brush('#004fc5'))dc.DrawRectangle(10, 105, 90, 60)dc.SetBrush(wx.Brush('#c50024'))dc.DrawRectangle(130, 105, 90, 60)dc.SetBrush(wx.Brush('#9e4757'))dc.DrawRectangle(250, 105, 90, 60)dc.SetBrush(wx.Brush('#5f3b00'))dc.DrawRectangle(10, 195, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c'))dc.DrawRectangle(130, 195, 90, 60)dc.SetBrush(wx.Brush('#785f36'))dc.DrawRectangle(250, 195, 90, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101102536628

我们画出九个长方形,并用不同的颜色填充。

dc.SetBrush(wx.Brush('#c56c00'))
dc.DrawRectangle(10, 15, 90, 60)

我们用十六进制符号指定笔刷的颜色。画笔就是形状的背景填充。然后我们用DrawRectangle()方法绘制矩形。

wx.Pen

Pen是一个基本的图形对象,它用于绘制矩形、椭圆形、多边形或其他形状的线条、曲线和轮廓。

wx.Pen(wx.Colour colour, width=1, style=wx.SOLID)

wx.Pen构造函数有三个参数:颜色、宽度和样式。下面是一个笔的样式列表。

  • wx.SOLID
  • wx.DOT
  • wx.LONG_DASH
  • wx.SHORT_DASH
  • wx.DOT_DASH
  • wx.TRANSPARENT
#pens.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Pens")self.Centre()def OnPaint(self, event):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.SOLID))dc.DrawRectangle(10, 15, 90, 60)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.DOT))dc.DrawRectangle(130, 15, 90, 60)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.LONG_DASH))dc.DrawRectangle(250, 15, 90, 60)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.SHORT_DASH))dc.DrawRectangle(10, 105, 90, 60)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.DOT_DASH))dc.DrawRectangle(130, 105, 90, 60)dc.SetPen(wx.Pen('#4c4c4c', 1, wx.TRANSPARENT))dc.DrawRectangle(250, 105, 90, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101103120278

如果我们没有指定自定义画笔,则使用默认的画笔,默认的画笔是wx.WHITE_BRUSH。默认的笔刷是wx.WHITE_BRUSH。矩形的周长是由钢笔绘制的。最后一个矩形没有边框。它是透明的,即不可见。

Join和Cap

一个钢笔对象有两个额外的参数:join和cap。连接定义了线条之间的连接方式。连接样式有以下选项。

wx.JOIN_MITER

wx.JOIN_BEVEL

wx.JOIN_ROUND

当使用 wx.JOIN_MITER 时,线条的外缘会被延伸。它们以一定的角度相遇,这个区域被填充。在 wx.JOIN_BEVEL 中,两条线之间的三角形缺口被填充。在wx.JOIN_ROUND中,填充了两条线之间的圆弧。默认值是wx.JOIN_ROUND。

cap定义了钢笔如何绘制线端。选项有

  • wx.CAP_ROUND
  • wx.CAP_PROJECTING
  • wx.CAP_BUTT

wx.CAP_ROUND画的是圆形的两端。wx.CAP_PROJECTINGwx.CAP_BUTT画的是方形端点。它们之间的区别是wx.CAP_PROJECTING会超出端点一半的线条尺寸。wx.CAP_ROUND也会超出端点。

#joins_caps.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Joins and caps")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)pen = wx.Pen('#4c4c4c', 10, wx.SOLID)pen.SetJoin(wx.JOIN_MITER)dc.SetPen(pen)dc.DrawRectangle(15, 15, 80, 50)pen.SetJoin(wx.JOIN_BEVEL)dc.SetPen(pen)dc.DrawRectangle(125, 15, 80, 50)pen.SetJoin(wx.JOIN_ROUND)dc.SetPen(pen)dc.DrawRectangle(235, 15, 80, 50)pen.SetCap(wx.CAP_BUTT)dc.SetPen(pen)dc.DrawLine(30, 150,  150, 150)pen.SetCap(wx.CAP_PROJECTING)dc.SetPen(pen)dc.DrawLine(30, 190,  150, 190)pen.SetCap(wx.CAP_ROUND)dc.SetPen(pen)dc.DrawLine(30, 230,  150, 230)pen2 = wx.Pen('#4c4c4c', 1, wx.SOLID)dc.SetPen(pen2)dc.DrawLine(30, 130, 30, 250)dc.DrawLine(150, 130, 150, 250)dc.DrawLine(155, 130, 155, 250)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
pen = wx.Pen('#4c4c4c', 10, wx.SOLID)
image-20201101103337422

为了看到各种join和cap样式,我们需要将笔的宽度设置为大于1。

dc.DrawLine(150, 130, 150, 250)
dc.DrawLine(155, 130, 155, 250)

请注意两条包围的竖线。它们之间的距离是5px。这正好是当前笔宽的一半。

渐变

渐变是指从浅色到深色或从一种颜色到另一种颜色的平滑混合。在二维绘图程序和绘画程序中,渐变用于创建多彩的背景和特殊效果,以及模拟灯光和阴影。

GradientFillLinear(self, rect, initialColour, destColour, nDirection=RIGHT)

此方法用线性渐变填充 "rect "指定的区域,从 "initialColour "开始,最终渐变到 "destColour"。nDirection参数指定颜色变化的方向,默认值是wx.EAST

#gradients.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Gradients")self.Centre()def OnPaint(self, event):dc = wx.PaintDC(self)dc.GradientFillLinear((20, 20, 180, 40), '#ffec00', '#000000', wx.NORTH)dc.GradientFillLinear((20, 80, 180, 40), '#ffec00', '#000000', wx.SOUTH)dc.GradientFillLinear((20, 140, 180, 40), '#ffec00', '#000000', wx.EAST)dc.GradientFillLinear((20, 200, 180, 40), '#ffec00', '#000000', wx.WEST)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101103621870

在例子中,四个矩形被填充了渐变。

wx.Brush

Brush是一个基本的图形对象。它用于绘制图形形状的背景,如矩形、椭圆或多边形。

wxPython内置了以下笔刷类型。

  • wx.SOLID
  • wx.STIPPLE
  • wx.BDIAGONAL_HATCH
  • wx.CROSSDIAG_HATCH
  • wx.FDIAGONAL_HATCH
  • wx.CROSS_HATCH
  • wx.HORIZONTAL_HATCH
  • wx.VERTICAL_HATCH
  • wx.TRANSPARENT
#brushes.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Brushes")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetBrush(wx.Brush('#4c4c4c', wx.CROSS_HATCH))dc.DrawRectangle(10, 15, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.SOLID))dc.DrawRectangle(130, 15, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.BDIAGONAL_HATCH))dc.DrawRectangle(250, 15, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.CROSSDIAG_HATCH))dc.DrawRectangle(10, 105, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.FDIAGONAL_HATCH))dc.DrawRectangle(130, 105, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.HORIZONTAL_HATCH))dc.DrawRectangle(250, 105, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.VERTICAL_HATCH))dc.DrawRectangle(10, 195, 90, 60)dc.SetBrush(wx.Brush('#4c4c4c', wx.TRANSPARENT))dc.DrawRectangle(130, 195, 90, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101103835780

例子中使用了八种不同的内置笔刷类型。

Custom Patterns

我们不受限于使用预定义的模式。我们可以轻松地创建自己的自定义模式。

#custom_patterns.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Custom patterns")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('#C7C3C3'))brush1 = wx.Brush(wx.Bitmap('pattern1.png'))dc.SetBrush(brush1)dc.DrawRectangle(10, 15, 90, 60)brush2 = wx.Brush(wx.Bitmap('pattern2.png'))dc.SetBrush(brush2)dc.DrawRectangle(130, 15, 90, 60)brush3 = wx.Brush(wx.Bitmap('pattern3.png'))dc.SetBrush(brush3)dc.DrawRectangle(250, 15, 90, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101104057139
brush1 = wx.Brush(wx.Bitmap('pattern1.png'))
dc.SetBrush(brush1)
dc.DrawRectangle(10, 15, 90, 60)

刷子是由位图创建的,它用于填充一个矩形的内部。

Points

最简单的几何物体是一个点。它是窗口上的一个普通点。

DrawPoint(self, x, y)

此方法在x、y坐标处画一个点。

#points.pyimport wx
import randomclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Points")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('RED'))for i in range(1000):w, h = self.GetSize()x = random.randint(1, w-1)y = random.randint(1, h-1)dc.DrawPoint(x, y)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101104319232

单点可能很难看到,所以我们创造1000点。

dc.SetPen(wx.Pen('RED'))

这里我们将笔的颜色设置为红色。

w, h = self.GetSize()
x = random.randint(1, w-1)

这些点在窗口的客户端区域周围随机分布。它们也是动态分布的。如果我们调整窗口的大小,这些点将在新的客户端大小上随机抽取。randint(a, b)方法返回一个范围为[a, b]的随机整数,例如,包括两个点。

Shapes

shape是比较复杂的几何图形对象。我们在下面的例子中画出各种几何图形。

#shapes.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Shapes")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetBrush(wx.Brush('#777'))dc.SetPen(wx.Pen("#777"))dc.DrawEllipse(20, 20, 90, 60)dc.DrawRoundedRectangle(130, 20, 90, 60, 10)dc.DrawArc(240, 40, 340, 40, 290, 20)dc.DrawRectangle(20, 120, 80, 50)dc.DrawPolygon(((130, 140), (180, 170), (180, 140), (220, 110), (140, 100)))dc.DrawSpline(((240, 170), (280, 170), (285, 110), (325, 110)))dc.DrawLines(((20, 260), (100, 260), (20, 210), (100, 210)))dc.DrawCircle(170, 230, 35)dc.DrawRectangle(250, 200, 60, 60)def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101110119404

在我们的例子中,我们画了一个椭圆、一个圆角矩形、一个弧形、一个矩形、一个多边形、S型线、线条、一个圆和一个正方形。圆是一种特殊的椭圆,正方形是一种特殊的矩形。

Regions

一个区域可以是任何形状,如矩形或圆形。通过 "Union"、"Intersect"、"Substract "和 "Xor "操作,我们可以创建复杂的区域。区域用于勾勒、填充和剪切。

我们可以用三种方式创建区域。最简单的方法是创建一个矩形区域。更复杂的区域可以从位图的点列表中创建。

在我们进入区域之前,我们将先创建一个小例子。

#lines.pyimport wx
from math import hypot, sin, cos, piclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle('Lines')self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)size_x, size_y = self.GetClientSize()dc.SetDeviceOrigin(size_x/2, size_y/2)radius = hypot(size_x/2, size_y/2)angle = 0while (angle < 2*pi):x = radius*cos(angle)y = radius*sin(angle)dc.DrawLine((0, 0), (x, y))angle = angle + 2*pi/360def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101110419096

在这个例子中,我们从客户端区域的中间画出360条线。两条线之间的距离是1度。

import wx
from math import hypot, sin, cos, pi

我们需要三个数学函数和一个数学模块的常数。

dc.SetDeviceOrigin(size_x/2, size_y/2)

方法SetDeviceOrigin()创建了一个新的坐标系起点。我们把它放到客户端区域的中间。通过重新定位坐标系,我们使我们的绘图不那么复杂。

radius = hypot(size_x/2, size_y/2)

在这里,我们得到了斜边。它是最长的线,我们可以从客户端区域的中间绘制。它是应该从开始画到窗口角落的线的长度。

x = radius*cos(angle)
y = radius*sin(angle)

这些是参数函数。它们用来寻找曲线上的[x,y]点。所有的360线都是从坐标系的起点开始画到圆上的点。

Clipping

"剪切 "是将绘图限制在某一区域。剪裁通常用于创建效果和提高应用程序的性能。我们通过SetClippingRegionAsRegion()方法将绘图限制在某个区域。

在下面的例子中,我们将对之前的程序进行修改和增强。

#star.pyimport wx
from math import hypot, sin, cos, piclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Star")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('#424242'))size_x, size_y = self.GetClientSize()dc.SetDeviceOrigin(size_x/2, size_y/2)points = (((0, 85), (75, 75), (100, 10), (125, 75), (200, 85),(150, 125), (160, 190), (100, 150), (40, 190), (50, 125)))region = wx.Region(points)dc.SetDeviceClippingRegion(region)radius = hypot(size_x/2, size_y/2)angle = 0while (angle < 2*pi):x = radius*cos(angle)y = radius*sin(angle)dc.DrawLine((0, 0), (x, y))angle = angle + 2*pi/360dc.DestroyClippingRegion()def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101110734081

我们再次画出所有的360线。但这次只画出了一部分。我们限制绘制的区域是一个五角星。

region = wx.Region(points)
dc.SetDeviceClippingRegion(region)

我们从点的列表中创建一个区域。SetDeviceClippingRegion()方法将绘图限制在指定的区域内。在我们的例子中,它是一个星形对象。

dc.DestroyClippingRegion()

我们必须destroy剪切区域。

Region 操作

区域可以组合成更复杂的形状。我们可以使用四种集合操作: union, intersect, substract, xor.

下面的例子展示了所有这四种操作的操作。

#region_operations.pyimport wxclass Example(wx.Frame):def __init__(self, *args, **kw):super(Example, self).__init__(*args, **kw)self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.SetTitle("Regions")self.Centre()def OnPaint(self, e):dc = wx.PaintDC(self)dc.SetPen(wx.Pen('#d4d4d4'))dc.DrawRectangle(20, 20, 50, 50)dc.DrawRectangle(30, 40, 50, 50)dc.SetBrush(wx.Brush('#ffffff'))dc.DrawRectangle(100, 20, 50, 50)dc.DrawRectangle(110, 40, 50, 50)region1 = wx.Region(100, 20, 50, 50)region2 = wx.Region(110, 40, 50, 50)region1.Intersect(region2)rect = region1.GetBox()dc.SetDeviceClippingRegion(region1)dc.SetBrush(wx.Brush('#ff0000'))dc.DrawRectangle(rect)dc.DestroyClippingRegion()dc.SetBrush(wx.Brush('#ffffff'))dc.DrawRectangle(180, 20, 50, 50)dc.DrawRectangle(190, 40, 50, 50)region1 = wx.Region(180, 20, 50, 50)region2 = wx.Region(190, 40, 50, 50)region1.Union(region2)dc.SetDeviceClippingRegion(region1)rect = region1.GetBox()dc.SetBrush(wx.Brush('#fa8e00'))dc.DrawRectangle(rect)dc.DestroyClippingRegion()dc.SetBrush(wx.Brush('#ffffff'))dc.DrawRectangle(20, 120, 50, 50)dc.DrawRectangle(30, 140, 50, 50)region1 = wx.Region(20, 120, 50, 50)region2 = wx.Region(30, 140, 50, 50)region1.Xor(region2)rect = region1.GetBox()dc.SetDeviceClippingRegion(region1)dc.SetBrush(wx.Brush('#619e1b'))dc.DrawRectangle(rect)dc.DestroyClippingRegion()dc.SetBrush(wx.Brush('#ffffff'))dc.DrawRectangle(100, 120, 50, 50)dc.DrawRectangle(110, 140, 50, 50)region1 = wx.Region(100, 120, 50, 50)region2 = wx.Region(110, 140, 50, 50)region1.Subtract(region2)rect = region1.GetBox()dc.SetDeviceClippingRegion(region1)dc.SetBrush(wx.Brush('#715b33'))dc.DrawRectangle(rect)dc.DestroyClippingRegion()dc.SetBrush(wx.Brush('#ffffff'))dc.DrawRectangle(180, 120, 50, 50)dc.DrawRectangle(190, 140, 50, 50)region1 = wx.Region(180, 120, 50, 50)region2 = wx.Region(190, 140, 50, 50)region2.Subtract(region1)rect = region2.GetBox()dc.SetDeviceClippingRegion(region2)dc.SetBrush(wx.Brush('#0d0060'))dc.DrawRectangle(rect)dc.DestroyClippingRegion()def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101111519996

In the example, we present six region set operations.

 region1 = wx.Region(100, 20, 50, 50)region2 = wx.Region(110, 40, 50, 50)region1.Intersect(region2)

这段代码对两个区域进行交集运算。

映射模式

映射模式定义了用于将页面空间单位转换为设备空间单位的度量单位,还定义了设备的X轴和Y轴的方向。

逻辑和设备单位

如果我们在客户端区域上绘制文本或几何基元,我们会使用逻辑单位来定位它们。

如果我们要绘制一些文本,我们提供文本参数和 x、y 位置。x、y 是以逻辑单位为单位。然后设备以设备单位绘制文本。逻辑单位和设备单位可能相同,也可能不同。逻辑单位是人们使用的(毫米),设备单位是 "特定 "设备的本地单位。例如,一个屏幕的本地设备单位是像素。惠普LaserJet 1022的原生设备单位是1200dpi(dots per inch)。

设备的映射模式是一种将逻辑单位转换为设备单位的方法。wxPython有以下映射模式。

Mapping ModeLogical Unit
wx.MM_TEXT1 pixel
wx.MM_METRIC1 millimeter
wx.MM_LOMETRIC1/10 of a millimeter
wx.MM_POINTS1 point, 1/72 of an inch
wx.MM_TWIPS1/20 of a point or 1/1440 of an inch

默认的映射模式是wx.MM_TEXT。在这种模式下,逻辑单位和设备单位是一样的。当人们在屏幕上定位对象或设计网页时,他们通常以像素为单位进行思考。网页设计者创建三栏式网页,这些栏位的设置是以像素为单位的。一个页面的最低公分母通常是800 px等。这种思维很自然,因为我们知道我们的显示器有如1024x768 pxs。我们不会进行换算,而是习惯于以像素为单位进行思考。如果我们想以毫米为单位画一个结构,我们可以使用两种公制映射模式。直接以毫米为单位绘制对屏幕来说太粗了,这就是为什么我们有wx.MM_LOMETRIC映射模式。

要设置不同的贴图模式,我们使用SetMapMode()方法。

Ruler 例子

ruler以像素为单位测量屏幕对象。

#ruler.pyimport wxRW = 701 # ruler width
RM = 10  # ruler margin
RH = 80  # ruler heightclass Example(wx.Frame):def __init__(self, parent):wx.Frame.__init__(self, parent, size=(RW + 2*RM, RH),style=wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.STAY_ON_TOP)self.font = wx.Font(7, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD, False, 'Courier 10 Pitch')self.InitUI()def InitUI(self):self.Bind(wx.EVT_PAINT, self.OnPaint)self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)self.Bind(wx.EVT_MOTION, self.OnMouseMove)self.Centre()self.Show(True)def OnPaint(self, e):dc = wx.PaintDC(self)brush = wx.Brush(wx.Bitmap('granite.png'))dc.SetBrush(brush)dc.DrawRectangle(0, 0, RW+2*RM, RH)dc.SetFont(self.font)dc.SetPen(wx.Pen('#F8FF25'))dc.SetTextForeground('#F8FF25')for i in range(RW):if not (i % 100):dc.DrawLine(i+RM, 0, i+RM, 10)w, h = dc.GetTextExtent(str(i))dc.DrawText(str(i), i+RM-w/2, 11)elif not (i % 20):dc.DrawLine(i+RM, 0, i+RM, 8)elif not (i % 2):dc.DrawLine(i+RM, 0, i+RM, 4)def OnLeftDown(self, e):x, y = self.ClientToScreen(e.GetPosition())ox, oy = self.GetPosition()dx = x - oxdy = y - oyself.delta = ((dx, dy))def OnMouseMove(self, e):if e.Dragging() and e.LeftIsDown():self.SetCursor(wx.Cursor(wx.CURSOR_HAND))x, y = self.ClientToScreen(e.GetPosition())fp = (x - self.delta[0], y - self.delta[1])self.Move(fp)def OnLeftUp(self, e):self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))def OnRightDown(self, e):self.Close()def main():app = wx.App()ex = Example(None)ex.Show()app.MainLoop()if __name__ == '__main__':main()
image-20201101112246816

在这个例子中,我们创建一个标尺。这个尺子以像素为单位测量屏幕对象。我们保留了默认的映射模式,即wx.MM_TEXT。正如我们已经提到的,这种模式具有相同的逻辑和设备单位。在我们的例子中,这些单位是像素。

def __init__(self, parent):wx.Frame.__init__(self, parent, size=(RW + 2*RM, RH),style=wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.STAY_ON_TOP)

我们已经创建了一个无边框的窗口。尺子的宽度是721 px。RW + 2RM = 701 + 20 = 721. 标尺上显示700个数字;0 ... 700是701像素。尺子两边都有边距,210是20像素。合起来就是721个像素。

brush = wx.Brush(wx.Bitmap('granite.png'))
dc.SetBrush(brush)
dc.DrawRectangle(0, 0, RW+2*RM, RH)

这里我们在窗口上绘制一个自定义的图案。我们使用了Gimp中的一个预定义图案。它被称为花岗岩。

w, h = dc.GetTextExtent(str(i))
dc.DrawText(str(i), i+RM-w/2, 11)

这些行确保我们正确地对齐文本。GetTextExtent()方法返回文本的宽度和高度。

我们的窗口周围没有边框。所以我们必须手动处理移动。OnLeftDown()OnMouseMove()方法使我们能够移动标尺。

def OnLeftDown(self, e):x, y = self.ClientToScreen(e.GetPosition())ox, oy = self.GetPosition()dx = x - oxdy = y - oyself.delta = ((dx, dy))

OnLeftDown()方法中,我们确定了窗口和鼠标光标的坐标;delta值是鼠标指针离窗口左上角的距离。我们需要delta值来移动窗口。

def OnMouseMove(self, e):if e.Dragging() and e.LeftIsDown():self.SetCursor(wx.Cursor(wx.CURSOR_HAND))x, y = self.ClientToScreen(e.GetPosition())fp = (x - self.delta[0], y - self.delta[1])self.Move(fp)

当我们同时拖动窗口并按下鼠标左键时,该代码就会被执行。在代码块中,我们用SetCursor()改变鼠标光标,用Move()方法移动窗口。用delta值来获取距离。

def OnLeftUp(self, e):self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))

当我们释放鼠标左键时,我们将光标变回箭头。

def OnRightDown(self, e):self.Close()

右键点击窗口区域,即可关闭窗口。

这篇关于wxpython入门第八步(画图)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/248192

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

MySQL入门到精通

一、创建数据库 CREATE DATABASE 数据库名称; 如果数据库存在,则会提示报错。 二、选择数据库 USE 数据库名称; 三、创建数据表 CREATE TABLE 数据表名称; 四、MySQL数据类型 MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串类型 4.1 数值类型 数值类型 类型大小用途INT4Bytes整数值FLOAT4By

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是