在 PyCairo 教程中的这个部分,我们将会绘制一些基本的元素。我们将绘制简单的直线,应用填充和 stroke 操作,我们将会讨论虚线,线帽和线的交合。
直线
直线是非常基本的向量对象。为了绘制一条直线,我们使用两个方法调用。一个是move_to()调用,我们需要把起点位置传给它。另一个是line_to(),我们则需要把直线的终点传给它。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
In this program, we connect all mouse
clicks with a line.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
import gtk
import cairo
class MouseButtons:
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
class MainWindow(gtk.Window):
def __init__(self):
super(self.__class__, self).__init__()
self.init_ui()
print "MainWindow: " + str(self)
def init_ui(self):
self.darea = gtk.DrawingArea()
self.darea.connect("expose_event", self.expose)
self.darea.set_events(gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.BUTTON_RELEASE_MASK)
self.add(self.darea)
self.coords = []
self.darea.connect("button-press-event", self.on_button_press)
self.set_title("Lines")
self.resize(300, 200)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("delete-event", gtk.main_quit)
self.show_all()
def on_button_press(self, widget, event):
if event.type == gtk.gdk.BUTTON_PRESS and event.button == MouseButtons.LEFT_BUTTON:
self.coords.append([event.x, event.y])
if event.type == gtk.gdk.BUTTON_PRESS and event.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
pass
def expose(self, widget, event):
self.context = widget.window.cairo_create()
self.on_draw(300, self.context)
def on_draw(self, wid, cr):
cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)
for i in self.coords:
for j in self.coords:
cr.move_to (i[0], i[1])
cr.line_to (j[0], j[1])
cr.stroke()
del self.coords[:]
def main():
window = MainWindow()
gtk.main()
if __name__ == "__main__":
main()
在我们的例子中,我们用鼠标左键随机的在窗口中点击。每一次点击的位置都会被存进一个列表。当我们在窗口中按下鼠标右键时,列表中每一个点都会与其他所有的点连接起来。通过这种方式,我们可以创建一些有趣的对象。再次点击鼠标右键可以清空窗口,我们就可以创建另一个对象了。
class MouseButtons:
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
GTK 文档简单的状态标记,鼠标左键为数字1,鼠标右键为数字3。我们创建一个定制的类来为鼠标的键做标识。
self.darea.set_events(gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.BUTTON_RELEASE_MASK)
某些事件默认情况下是不打开的。鼠标按钮按下事件即属其中。然而,我们需要打开鼠标按钮按下事件。
self.darea.connect("button-press-event", self.on_button_press)
在这个例子中,我们对鼠标按下事件作出反应。
cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)
将用黑色墨水和0.5点宽度来画直线。
for i in self.coords:
for j in self.coords:
cr.move_to (i[0], i[1])
cr.line_to (j[0], j[1])
cr.stroke()
我们把列表中每一个点连接到其他的每一个点。stroke()调用绘制直线。
del self.coords[:]
最后,所有的坐标被删除。现在我们可以创建另一个对象了。
def on_button_press(self, widget, event):
if event.type == gtk.gdk.BUTTON_PRESS and event.button == MouseButtons.LEFT_BUTTON:
self.coords.append([event.x, event.y])
如果按下了鼠标左按钮,我们会把它的x,y坐标添加到self.coords列表中去。
if event.type == gtk.gdk.BUTTON_PRESS and event.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
按下鼠标右键时,我们调用queue_draw()方法,它将会渲染绘制区域。所有的点会连成线。
Figure: Lines
填充和stroke
Stroke操作绘制形状的轮廓,而填充操作则填充形状的内部。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example draws a circle
using the PyCairo library
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
import cairo
import gtk
import math
class MainWindow(gtk.Window):
def __init__(self):
super(self.__class__, self).__init__()
self.init_ui()
def init_ui(self):
self.darea = gtk.DrawingArea()
self.darea.connect("expose_event", self.expose)
self.add(self.darea)
self.set_title("Fill & stroke")
self.resize(230, 150)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("delete-event", gtk.main_quit)
self.show_all()
def expose(self, widget, event):
self.context = widget.window.cairo_create()
self.on_draw(230, self.context)
def on_draw(self, wid, cr):
cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)
width, height = self.get_size()
cr.translate(width/2, height/2)
cr.arc(0, 0, 50, 0, 2 * math.pi)
cr.stroke_preserve()
cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()
def main():
window = MainWindow()
gtk.main()
if __name__ == "__main__":
main()
在我们的例子中,我们将画一个圆圈,然后用一种颜色填充它。
import math
pi常量需要这个模块,后面在绘制圆圈时会用到pi。
cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)
我们用set_line_width()方法设置线的宽度。我们使用set_source_rgb()把source设为某种暗红色。
width, height = self.get_size()
此处我们获取窗口的宽度和高度。我们需要这些值来将圆圈放到窗口的中心位置。
cr.translate(width/2, height/2)
cr.arc(0, 0, 50, 0, 2 * math.pi)
cr.stroke_preserve()
通过translate()方法,我们将绘制的原点移动到窗口的中心位置。我们想要将我们的圆圈放在中心位置。arc()方法向cairo绘制上下文中添加一个新的圆圈的path。最后,stroke_preserve()方法绘制圆圈的轮廓。不像stroke()方法,它会保留形状以用于后面的绘制。
cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()
我们换一种颜色来画,使用fill()方法并用一种新的颜色来填充圆圈。
Figure: Fill & stroke
Pen dashes
每一条直线都可以用不同的pen dash来画。一个pen dash定义了直线的style。dash模式由set_dash()方法指定。模式由一个浮点值组成的dash列表来描述。它们设置了dash模式的on和off部分。dash被stroke()方法用来创建一条直线。如果dashes的数量为0,则dashing是被关掉的。如果dashes的数量为1,则假设是一个对称的模式,其中on和off部分交替出现,它们的大小由dashes中的那个值来指定。
def on_draw(self, wid, cr):
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(2)
cr.set_dash([4.0, 21.0, 2.0])
cr.move_to(40, 30)
cr.line_to(250, 30)
cr.stroke()
cr.set_dash([14.0, 6.0])
cr.move_to(40, 50)
cr.line_to(250, 50)
cr.stroke()
cr.set_dash([1.0])
cr.move_to(40, 70)
cr.line_to(250, 70)
cr.stroke()
我们用三种不同类型的pen dashes画了三条直线。
cr.set_dash([4.0, 21.0, 2.0])
我们定义了具有三个数字的模式。我们画4个点,21个不画,然后2个点画。接下来是4个点不画,21个点画和2个点不画。这个模式如此交替,直到直线的终点。
cr.set_dash([14.0, 6.0])
在这个模式中,我们总是让14个点画,6个不画。
cr.set_dash([1.0])
我们在这儿创建了一个对称的pen dash模式,交替的一个点画,一个点不画。
Figure: Pen dashes
Line caps
Line caps 是直线的终点。
- cairo.LINE_CAP_BUTT
- cairo.LINE_CAP_ROUND
- cairo.LINE_CAP_SQUARE
Cairo中有三种不同的line cap style。
Figure: Square, round and butt caps
相对于具有一个cairo.LINE_CAP_BUTT cap的直线,具有一个cairo.LINE_CAP_SQUARE cap的直线将有不一样的大小。如果一条直线x单元宽,具有一个cairo.LINE_CAP_SQUARE cap的直线将会要大整整x个单元。开始处x/2个单元,结尾处x/2个单元。
def on_draw(self, wid, cr):
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(12)
cr.set_line_cap(cairo.LINE_CAP_BUTT)
cr.move_to(30, 50)
cr.line_to(150, 50)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_SQUARE)
cr.move_to(30, 130)
cr.line_to(150, 130)
cr.stroke()
cr.set_line_width(1.5)
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
cr.move_to(150, 35)
cr.line_to(150, 145)
cr.stroke()
cr.move_to(155, 35)
cr.line_to(155, 145)
cr.stroke()
这个例子画了三条具有不同的line caps的直线。它也通过画三条额外的垂直方向的细线生动的演示了直线不同大小的影响。
cr.set_line_width(12)
我们的直线将是12个单元宽的。默认的直线宽度为2.
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
此处我们画了一条具有一个cairo.LINE_CAP_ROUND cap的水平直线。
cr.set_line_width(1.5)
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
这是用于演示不同直线大小的影响的三条垂直直线之一。
Figure: Line caps
Line joins
直线可以用三种不同的联合style来做联合。
- cairo.LINE_JOIN_MITER
- cairo.LINE_JOIN_BEVEL
- cairo.LINE_JOIN_ROUND
Figure: Bevel, Round, Miter line joins
def on_draw(self, wid, cr):
cr.set_line_width(14)
cr.rectangle(30, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
cr.rectangle(160, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_BEVEL)
cr.stroke()
cr.rectangle(100, 160, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_ROUND)
cr.stroke()
在这个例子中,我们画了三个具有不同的线联合的厚矩形。
cr.set_line_width(14)
直线是14个单元宽的。
cr.rectangle(30, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
此处我们画了一个有着 cairo.LINE_JOIN_MITER联合风格的矩形。
Figure: Line joins
PyCairo指南的这一章中,我们做了一些基本的绘图。
Done.