15 PyGTK 中的自定义部件
你是否曾经看到一个程序就在想,一个特别的GUI项目是怎样被创建的呢?或许每个程序员都想知道。然后你就看看你最喜欢的GUI库提供的部件列表。但是你找不到它。工具包一般只提供最常见的部件,例如按钮,文本部件,滑动器等等。没有哪个工具包提供所有可能的部件。
实际上有两种工具包——简朴的工具包和重量级的工具包。FLTK工具包是一种简朴的工具包。它仅仅提供最基本的部件和呈现方法,但是程序员可以自己创建一个更加复杂的部件。wxWidgets是一个重量级的工具包,它有相当多的部件。但是它也不提供更加专业的部件。例如一个速度仪表部件,一个用来检测CD能被烧制的容量的部件(例如:在nero中就有)。工具包也通常不会有图表。
程序员必须通过他们自己来创建一个这样的部件。他们可以通过工具包中提供的绘画工具做到。这里有两种可行的方法。一是,程序员可以更改或者增强现有的部件;或者二是,程序员从零起创建一个部件。
Burning widget
这是一个我们从零起创建的一个部件的例子。这个部件能够在各种媒体烧制程序中见到,就像Nero Burning ROM。
Code:burning.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
# ZetCode PyGTK tutorial
#
# This example creates a burning
# custom widget
#
# author: Jan Bodnar
# website: zetcode.com
# last edited: April 2011
import gtk
import cairo
class Burning(gtk.DrawingArea):
def __init__(self, parent):
self.par = parent
super(Burning, self).__init__()
self.num = ( "75", "150", "225", "300",
"375", "450", "525", "600", "675" )
self.set_size_request(-1, 30)
self.connect("expose-event", self.expose)
def expose(self, widget, event):
cr = widget.window.cairo_create()
cr.set_line_width(0.8)
cr.select_font_face("Courier",
cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(11)
width = self.allocation.width
self.cur_width = self.par.get_cur_value()
step = round(width / 10.0)
till = (width / 750.0) * self.cur_width
full = (width / 750.0) * 700
if (self.cur_width >= 700):
cr.set_source_rgb(1.0, 1.0, 0.72)
cr.rectangle(0, 0, full, 30)
cr.save()
cr.clip()
cr.paint()
cr.restore()
cr.set_source_rgb(1.0, 0.68, 0.68)
cr.rectangle(full, 0, till-full, 30)
cr.save()
cr.clip()
cr.paint()
cr.restore()
else:
cr.set_source_rgb(1.0, 1.0, 0.72)
cr.rectangle(0, 0, till, 30)
cr.save()
cr.clip()
cr.paint()
cr.restore()
cr.set_source_rgb(0.35, 0.31, 0.24)
for i in range(1, len(self.num) + 1):
cr.move_to(i*step, 0)
cr.line_to(i*step, 5)
cr.stroke()
(x, y, width, height, dx, dy) = cr.text_extents(self.num[i-1])
cr.move_to(i*step-width/2, 15)
cr.text_path(self.num[i-1])
cr.stroke()
class PyApp(gtk.Window):
def __init__(self):
super(PyApp, self).__init__()
self.set_title("Burning")
self.set_size_request(350, 200)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("destroy", gtk.main_quit)
self.cur_value = 0
vbox = gtk.VBox(False, 2)
scale = gtk.HScale()
scale.set_range(0, 750)
scale.set_digits(0)
scale.set_size_request(160, 40)
scale.set_value(self.cur_value)
scale.connect("value-changed", self.on_changed)
fix = gtk.Fixed()
fix.put(scale, 50, 50)
vbox.pack_start(fix)
self.burning = Burning(self)
vbox.pack_start(self.burning, False, False, 0)
self.add(vbox)
self.show_all()
def on_changed(self, widget):
self.cur_value = widget.get_value()
self.burning.queue_draw()
def get_cur_value(self):
return self.cur_value
PyApp()
gtk.main()
我们在窗口的底部放置了以DrawingArea区域,准备手动绘制整个部件。所有重要的代码都属于Burning类的expose()方法。这个部件以图像的形式展现了一个媒体总的容量和空余可用的空间。这个部件被一个标尺部件所控制。我们自定义部件的最小值为0,最大值为750。如果我们到达值700,我们就开始用红色绘制它。这个一般在提示超容量烧录了。
self.num = ( "75", "150", "225", "300", "375", "450", "525", "600", "675" )
这些数字会在burning部件上显示。它们指示的是一个媒体的容量。
self.cur_width = self.par.get_cur_value()
这两行是从标尺部件上获取当前的数据。我们从父部件获得了父部件,然后我们获得了当前值。
till = (width / 750.0) * self.cur_width full = (width / 750.0) * 700
till参数是判定将要绘制的总共大小。这个值来自滑动器部件。它是整个区域的一个比例。fill参数是判定我们从哪里开始用红色来绘制。
cr.set_source_rgb(1.0, 1.0, 0.72) cr.rectangle(0, 0, till, 30) cr.save() cr.clip() cr.paint() cr.restore()
这些代码这里在媒体满值之前绘制了一个黄色的矩形。
(x, y, width, height, dx, dy) = cr.text_extents(self.num[i-1]) cr.move_to(i*step-width/2, 15) cr.text_path(self.num[i-1]) cr.stroke()
这些代码这里在burning部件上绘制数字。我们将正确的计算TextExtents到文本的位置。
def on_changed(self, widget): self.cur_value = widget.get_value() self.burning.queue_draw()
我们从标尺部件获得值,将其存储在cur_value变量中留待后用。我们重画burning部件。
Figure:Burning Widget
在这章中,我们用PyGTK创建了一个自定义的部件。