原文: http://zetcode.com/gui/pygtk/drawingII/

在 PyGTK 编程教程的这一部分中,我们将继续使用 Cairo 库进行绘制。

甜甜圈

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。

donut.py

  1. #!/usr/bin/python
  2. # ZetCode PyGTK tutorial
  3. #
  4. # This program creates a donut
  5. # with cairo library
  6. #
  7. # author: jan bodnar
  8. # website: zetcode.com
  9. # last edited: February 2009
  10. import gtk
  11. import math
  12. class PyApp(gtk.Window):
  13. def __init__(self):
  14. super(PyApp, self).__init__()
  15. self.set_title("Donut")
  16. self.set_size_request(350, 250)
  17. self.set_position(gtk.WIN_POS_CENTER)
  18. self.connect("destroy", gtk.main_quit)
  19. darea = gtk.DrawingArea()
  20. darea.connect("expose-event", self.expose)
  21. self.add(darea)
  22. self.show_all()
  23. def expose(self, widget, event):
  24. cr = widget.window.cairo_create()
  25. cr.set_line_width(0.5)
  26. w = self.allocation.width
  27. h = self.allocation.height
  28. cr.translate(w/2, h/2)
  29. cr.arc(0, 0, 120, 0, 2*math.pi)
  30. cr.stroke()
  31. for i in range(36):
  32. cr.save()
  33. cr.rotate(i*math.pi/36)
  34. cr.scale(0.3, 1)
  35. cr.arc(0, 0, 120, 0, 2*math.pi)
  36. cr.restore()
  37. cr.stroke()
  38. PyApp()
  39. gtk.main()

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

  1. cr.translate(w/2, h/2)
  2. cr.arc(0, 0, 120, 0, 2*math.pi)
  3. cr.stroke()

刚开始时有一个椭圆。

  1. for i in range(36):
  2. cr.save()
  3. cr.rotate(i*math.pi/36)
  4. cr.scale(0.3, 1)
  5. cr.arc(0, 0, 120, 0, 2*math.pi)
  6. cr.restore()
  7. cr.stroke()

旋转几圈后,有一个甜甜圈。 我们使用save()restore()方法将每个旋转和缩放操作彼此隔离。

Cario 绘图 II - 图1

图:多纳圈

渐变

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)

gradients.py

  1. #!/usr/bin/python
  2. # ZetCode PyGTK tutorial
  3. #
  4. # This program works with
  5. # gradients in cairo
  6. #
  7. # author: jan bodnar
  8. # website: zetcode.com
  9. # last edited: February 2009
  10. import gtk
  11. import cairo
  12. class PyApp(gtk.Window):
  13. def __init__(self):
  14. super(PyApp, self).__init__()
  15. self.set_title("Gradients")
  16. self.set_size_request(340, 390)
  17. self.set_position(gtk.WIN_POS_CENTER)
  18. self.connect("destroy", gtk.main_quit)
  19. darea = gtk.DrawingArea()
  20. darea.connect("expose-event", self.expose)
  21. self.add(darea)
  22. self.show_all()
  23. def expose(self, widget, event):
  24. cr = widget.window.cairo_create()
  25. lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)
  26. count = 1
  27. i = 0.1
  28. while i < 1.0:
  29. if count % 2:
  30. lg1.add_color_stop_rgba(i, 0, 0, 0, 1)
  31. else:
  32. lg1.add_color_stop_rgba(i, 1, 0, 0, 1)
  33. i = i + 0.1
  34. count = count + 1
  35. cr.rectangle(20, 20, 300, 100)
  36. cr.set_source(lg1)
  37. cr.fill()
  38. lg2 = cairo.LinearGradient(0.0, 0.0, 350.0, 0)
  39. count = 1
  40. i = 0.05
  41. while i < 0.95:
  42. if count % 2:
  43. lg2.add_color_stop_rgba(i, 0, 0, 0, 1)
  44. else:
  45. lg2.add_color_stop_rgba(i, 0, 0, 1, 1)
  46. i = i + 0.025
  47. count = count + 1
  48. cr.rectangle(20, 140, 300, 100)
  49. cr.set_source(lg2)
  50. cr.fill()
  51. lg3 = cairo.LinearGradient(20.0, 260.0, 20.0, 360.0)
  52. lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1)
  53. lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1)
  54. lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1)
  55. cr.rectangle(20, 260, 300, 100)
  56. cr.set_source(lg3)
  57. cr.fill()
  58. PyApp()
  59. gtk.main()

在我们的示例中,我们绘制了三个具有三个不同渐变的矩形。

  1. lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)

在这里,我们创建一个线性渐变图案。 参数指定直线,沿着该直线绘制渐变。 在我们的情况下,这是一条垂直线。

  1. lg3 = cairo.LinearGradient(20.0, 260.0, 20.0, 360.0)
  2. lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1)
  3. lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1)
  4. lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1)

我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该颜色将以大小的 1/10 停止。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘图,直到结束。

Cario 绘图 II - 图2

图:渐变

泡泡

在以下示例中,我们创建一个粉扑效果。 该示例将显示一个不断增长的居中文本,该文本将从某个点逐渐淡出。 这是一个非常常见的效果,您经常可以在 Flash 动画中看到它。

puff.py

  1. #!/usr/bin/python
  2. # ZetCode PyGTK tutorial
  3. #
  4. # This program creates a puff
  5. # effect
  6. #
  7. # author: jan bodnar
  8. # website: zetcode.com
  9. # last edited: February 2009
  10. import gtk
  11. import glib
  12. import cairo
  13. class PyApp(gtk.Window):
  14. def __init__(self):
  15. super(PyApp, self).__init__()
  16. self.set_title("Puff")
  17. self.resize(350, 200)
  18. self.set_position(gtk.WIN_POS_CENTER)
  19. self.connect("destroy", gtk.main_quit)
  20. self.darea = gtk.DrawingArea()
  21. self.darea.connect("expose-event", self.expose)
  22. self.add(self.darea)
  23. self.timer = True
  24. self.alpha = 1.0
  25. self.size = 1.0
  26. glib.timeout_add(14, self.on_timer)
  27. self.show_all()
  28. def on_timer(self):
  29. if not self.timer: return False
  30. self.darea.queue_draw()
  31. return True
  32. def expose(self, widget, event):
  33. cr = widget.window.cairo_create()
  34. w = self.allocation.width
  35. h = self.allocation.height
  36. cr.set_source_rgb(0.5, 0, 0)
  37. cr.paint()
  38. cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
  39. self.size = self.size + 0.8
  40. if self.size > 20:
  41. self.alpha = self.alpha - 0.01
  42. cr.set_font_size(self.size)
  43. cr.set_source_rgb(1, 1, 1)
  44. (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")
  45. cr.move_to(w/2 - width/2, h/2)
  46. cr.text_path("ZetCode")
  47. cr.clip()
  48. cr.stroke()
  49. cr.paint_with_alpha(self.alpha)
  50. if self.alpha <= 0:
  51. self.timer = False
  52. PyApp()
  53. gtk.main()

该示例在窗口上创建一个逐渐增长和褪色的文本。

  1. glib.timeout_add(14, self.on_timer)

每隔 14 毫秒调用一次on_timer()方法。

  1. def on_timer(self):
  2. if not self.timer: return False
  3. self.darea.queue_draw()
  4. return True

on_timer()方法中,我们在绘图区域上调用queue_draw()方法,该方法会触发曝光信号。

  1. cr.set_source_rgb(0.5, 0, 0)
  2. cr.paint()

我们将背景色设置为深红色。

  1. self.size = self.size + 0.8

每个周期,字体大小将增加 0.8 个单位。

  1. if self.size > 20:
  2. self.alpha = self.alpha - 0.01

字体大小大于 20 后开始淡出。

  1. (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")

我们得到了文本指标。

  1. cr.move_to(w/2 - width/2, h/2)

我们使用文本指标将文本放在窗口的中心。

  1. cr.text_path("ZetCode")
  2. cr.clip()

我们获取文本的路径,并为其设置当前的片段区域。

  1. cr.stroke()
  2. cr.paint_with_alpha(self.alpha)

我们绘制当前路径并考虑 alpha 值。

Cario 绘图 II - 图3

图:粉扑

反射

在下一个示例中,我们显示反射图像。 这种美丽的效果使人产生幻觉,好像图像在水中被反射一样。

reflection.py

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # ZetCode PyGTK tutorial
  4. #
  5. # This program creates an
  6. # image reflection
  7. #
  8. # author: Jan Bodnar
  9. # website: zetcode.com
  10. # last edited: April 2011
  11. import gtk
  12. import cairo
  13. import sys
  14. class PyApp(gtk.Window):
  15. def __init__(self):
  16. super(PyApp, self).__init__()
  17. self.set_title("Reflection")
  18. self.resize(300, 350)
  19. self.set_position(gtk.WIN_POS_CENTER)
  20. self.connect("destroy", gtk.main_quit)
  21. darea = gtk.DrawingArea()
  22. darea.connect("expose-event", self.expose)
  23. self.add(darea)
  24. try:
  25. self.surface = cairo.ImageSurface.create_from_png("slanec.png")
  26. except Exception, e:
  27. print e.message
  28. sys.exit(1)
  29. self.imageWidth = self.surface.get_width()
  30. self.imageHeight = self.surface.get_height()
  31. self.gap = 40
  32. self.border = 20
  33. self.show_all()
  34. def expose(self, widget, event):
  35. cr = widget.window.cairo_create()
  36. w = self.allocation.width
  37. h = self.allocation.height
  38. lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
  39. lg.add_color_stop_rgba(0, 0, 0, 0, 1)
  40. lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)
  41. cr.set_source(lg)
  42. cr.paint()
  43. cr.set_source_surface(self.surface, self.border, self.border)
  44. cr.paint()
  45. alpha = 0.7
  46. step = 1.0 / self.imageHeight
  47. cr.translate(0, 2 * self.imageHeight + self.gap)
  48. cr.scale(1, -1)
  49. i = 0
  50. while(i < self.imageHeight):
  51. cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1)
  52. i = i + 1
  53. cr.save()
  54. cr.clip()
  55. cr.set_source_surface(self.surface, self.border, self.border)
  56. alpha = alpha - step
  57. cr.paint_with_alpha(alpha)
  58. cr.restore()
  59. PyApp()
  60. gtk.main()

该示例显示了一个反射的城堡。

  1. lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
  2. lg.add_color_stop_rgba(0, 0, 0, 0, 1)
  3. lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)
  4. cr.set_source(lg)
  5. cr.paint()

背景充满了渐变的油漆。 涂料是从黑色到深灰色的平滑混合。

  1. cr.translate(0, 2 * self.imageHeight + self.gap)
  2. cr.scale(1, -1)

此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄一张照片并将其放在桌子上即可。 现在翻转它。

  1. cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1)
  2. i = i + 1
  3. cr.save()
  4. cr.clip()
  5. cr.set_source_surface(self.surface, self.border, self.border)
  6. alpha = alpha - step
  7. cr.paint_with_alpha(alpha)
  8. cr.restore()

这是最后一部分。 我们使第二个图像透明。 但是透明度不是恒定的。 图像逐渐淡出。 反射的图像逐行绘制。 clip()方法将图形限制为高度为 1 的矩形。paint_with_alpha()在绘制图像表面的当前片段时会考虑透明度。

Cario 绘图 II - 图4

图:反射

等待

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一条线在移动的错觉。 这种效果通常用于通知用户,一项艰巨的任务正在幕后进行。 一个示例是通过互联网流式传输视频。

waiting.py

  1. #!/usr/bin/python
  2. # ZetCode PyGTK tutorial
  3. #
  4. # This program creates an
  5. # waiting effect
  6. #
  7. # author: jan bodnar
  8. # website: zetcode.com
  9. # last edited: February 2009
  10. import gtk
  11. import glib
  12. import math
  13. import cairo
  14. trs = (
  15. ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
  16. ( 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ),
  17. ( 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 ),
  18. ( 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 ),
  19. ( 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 ),
  20. ( 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 ),
  21. ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 ),
  22. ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, )
  23. )
  24. class PyApp(gtk.Window):
  25. def __init__(self):
  26. super(PyApp, self).__init__()
  27. self.set_title("Waiting")
  28. self.set_size_request(250, 150)
  29. self.set_position(gtk.WIN_POS_CENTER)
  30. self.connect("destroy", gtk.main_quit)
  31. self.darea = gtk.DrawingArea()
  32. self.darea.connect("expose-event", self.expose)
  33. self.add(self.darea)
  34. self.count = 0
  35. glib.timeout_add(100, self.on_timer)
  36. self.show_all()
  37. def on_timer(self):
  38. self.count = self.count + 1
  39. self.darea.queue_draw()
  40. return True
  41. def expose(self, widget, event):
  42. cr = widget.window.cairo_create()
  43. cr.set_line_width(3)
  44. cr.set_line_cap(cairo.LINE_CAP_ROUND)
  45. w = self.allocation.width
  46. h = self.allocation.height
  47. cr.translate(w/2, h/2)
  48. for i in range(8):
  49. cr.set_source_rgba(0, 0, 0, trs[self.count%8][i])
  50. cr.move_to(0.0, -10.0)
  51. cr.line_to(0.0, -40.0)
  52. cr.rotate(math.pi/4)
  53. cr.stroke()
  54. PyApp()
  55. gtk.main()

我们用八个不同的 alpha 值绘制八条线。

  1. glib.timeout_add(100, self.on_timer)

我们使用计时器函数来创建动画。

  1. trs = (
  2. ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
  3. ...
  4. )

这是此演示中使用的透明度值的二维元组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。

  1. cr.set_line_width(3)
  2. cr.set_line_cap(cairo.LINE_CAP_ROUND)

我们使线条更粗一些,以便更好地显示它们。 我们用圆帽画线。 他们看起来更好。

  1. cr.set_source_rgba(0, 0, 0, trs[self.count%8][i]

在这里,我们定义了一条线的透明度值。

  1. cr.move_to(0.0, -10.0)
  2. cr.line_to(0.0, -40.0)
  3. cr.rotate(math.pi/4)
  4. cr.stroke()

这些代码行将绘制八行中的每行。

Cario 绘图 II - 图5

图:等待

在 PyGTK 编程库的这一章中,我们使用 Cairo 库进行了一些更高级的绘制。