1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Module Contents
24 ===============
25 This is the main module. It contains the base classes for chart widgets.
26 - class Chart: base class for all chart widgets.
27 - class ChartObject: base class for all things that can be drawn on a chart.
28 - class Background: background of a chart widget.
29 - class Title: title of a chart.
30
31 Colors
32 ------
33 All colors have to be C{(r, g, b)} tuples. The value of C{r, g} and C{b}
34 has to be between 0.0 and 1.0.
35 For example C{(0, 0, 0)} is black and C{(1, 1, 1)} is white.
36
37 Author: Sven Festersen (sven@sven-festersen.de)
38 """
39 __docformat__ = "epytext"
40 import cairo
41 import gobject
42 import gtk
43 import os
44 import pygtk
45
46 from pygtk_chart.basics import *
47
48
49 -class Chart(gtk.DrawingArea):
50 """
51 This is the base class for all chart widgets.
52 """
53
62
64 """
65 This method is called after the appearance of an object changed
66 and forces a redraw.
67 """
68 self.queue_draw()
69
70 - def expose(self, widget, event):
71 """
72 This method is called when an instance of Chart receives
73 the gtk expose_event.
74
75 @type widget: gtk.Widget
76 @param widget: The widget that received the event.
77 @type event: gtk.Event
78 @param event: The event.
79 """
80 self.context = widget.window.cairo_create()
81 self.context.rectangle(event.area.x, event.area.y, \
82 event.area.width, event.area.height)
83 self.context.clip()
84 self.draw(self.context)
85 return False
86
88 """
89 Draw basic things that every plot has (background, title, ...).
90
91 @type context: cairo.Context
92 @param context: The context to draw on.
93 @type rect: gtk.gdk.Rectangle
94 @param rect: A rectangle representing the charts area.
95 """
96 self.background.draw(context, rect)
97 self.title.draw(context, rect)
98
99 - def draw(self, context):
100 """
101 Draw the widget. This method is called automatically. Don't call it
102 yourself. If you want to force a redrawing of the widget, call
103 the queue_draw() method.
104
105 @type context: cairo.Context
106 @param context: The context to draw on.
107 """
108 rect = self.get_allocation()
109 context.set_line_width(1)
110 self.draw_basics(context, rect)
111
113 """
114 Saves the contents of the widget to svg file. The size of the image
115 will be the size of the widget.
116
117 @type filename: string
118 @param filename: The path to the file where you want the chart to be saved.
119 """
120 rect = self.get_allocation()
121 surface = cairo.SVGSurface(filename, rect.width, rect.height)
122 context = cairo.Context(surface)
123 self.draw(context)
124 surface.finish()
125
127 """
128 Saves the contents of the widget to png file. The size of the image
129 will be the size of the widget.
130
131 @type filename: string
132 @param filename: The path to the file where you want the chart to be saved.
133 """
134 rect = self.get_allocation()
135 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, rect.width,
136 rect.height)
137 context = cairo.Context(surface)
138 self.draw(context)
139 surface.write_to_png(filename)
140
141
143 """
144 This is the base class for all things that can be drawn in a chart,
145 e.g. title, axes, graphs,...
146 """
147
148 __gsignals__ = {"appearance-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])}
149
150
151 __gproperties__ = {"visible": (gobject.TYPE_BOOLEAN,
152 "visibilty of the object",
153 "Set whether to draw the object or not.",
154 True, gobject.PARAM_READWRITE),
155 "antialias": (gobject.TYPE_BOOLEAN,
156 "use antialiasing",
157 "Set whether to use antialiasing when drawing the object.",
158 True, gobject.PARAM_READWRITE)}
159
161 gobject.GObject.__init__(self)
162 self._show = True
163 self._antialias = True
164
166 if property.name == "visible":
167 return self._show
168 elif property.name == "antialias":
169 return self._antialias
170 else:
171 raise AttributeError, "Property %s does not exist." % property.name
172
174 if property.name == "visible":
175 self._show = value
176 elif property.name == "antialias":
177 self._antialias = value
178 else:
179 raise AttributeError, "Property %s does not exist." % property.name
180
182 """
183 A derived class should override this method. The drawing stuff
184 should happen here.
185
186 @type context: cairo.Context
187 @param context: The context to draw on.
188 @type rect: gtk.gdk.Rectangle
189 @param rect: A rectangle representing the charts area.
190 """
191 pass
192
193 - def draw(self, context, rect):
194 """
195 This method is called by the parent Chart instance. It
196 calls _do_draw.
197
198 @type context: cairo.Context
199 @param context: The context to draw on.
200 @type rect: gtk.gdk.Rectangle
201 @param rect: A rectangle representing the charts area.
202 """
203 if self._show:
204 if not self._antialias:
205 context.set_antialias(cairo.ANTIALIAS_NONE)
206 self._do_draw(context, rect)
207 context.set_antialias(cairo.ANTIALIAS_DEFAULT)
208
210 """
211 This method sets the antialiasing mode of the ChartObject. Antialiasing
212 is enabled by default.
213
214 @type antialias: boolean
215 @param antialias: If False, antialiasing is disabled for this
216 ChartObject.
217 """
218 self.set_property("antialias", antialias)
219 self.emit("appearance_changed")
220
222 return self.get_property("antialias")
223
225 """
226 Use this method to set whether the ChartObject should be visible or
227 not.
228
229 @type visible: boolean
230 @param visible: If False, the PlotObject won't be drawn.
231 """
232 self.set_property("visible", visible)
233 self.emit("appearance_changed")
234
236 return self.get_property("visible")
237
238
239 gobject.type_register(ChartObject)
240
241
243 """
244 The background of a chart.
245 """
246
247 __gproperties__ = {"color": (gobject.TYPE_PYOBJECT,
248 "background color",
249 "The color of the backround in (r,g,b) format. r,g,b in [0,1]",
250 gobject.PARAM_READWRITE),
251 "gradient": (gobject.TYPE_PYOBJECT,
252 "background gradient",
253 "A background gardient. (first_color, second_color)",
254 gobject.PARAM_READWRITE),
255 "image": (gobject.TYPE_STRING,
256 "background image file",
257 "Path to the image file to use as background.",
258 "", gobject.PARAM_READWRITE)}
259
261 ChartObject.__init__(self)
262 self._color = (1, 1, 1)
263 self._gradient = None
264 self._image = ""
265 self._image_surface = None
266
268 if property.name == "visible":
269 return self._show
270 elif property.name == "antialias":
271 return self._antialias
272 elif property.name == "gradient":
273 return self._gradient
274 elif property.name == "color":
275 return self._color
276 elif property.name == "image":
277 return self._image
278 else:
279 raise AttributeError, "Property %s does not exist." % property.name
280
282 if property.name == "visible":
283 self._show = value
284 elif property.name == "antialias":
285 self._antialias = value
286 elif property.name == "gradient":
287 self._gradient = value
288 elif property.name == "color":
289 self._color = value
290 elif property.name == "image":
291 self._image = value
292 else:
293 raise AttributeError, "Property %s does not exist." % property.name
294
296 """
297 Do all the drawing stuff.
298
299 @type context: cairo.Context
300 @param context: The context to draw on.
301 @type rect: gtk.gdk.Rectangle
302 @param rect: A rectangle representing the charts area.
303 """
304 if self._color != None:
305
306 c = self._color
307 context.set_source_rgb(c[0], c[1], c[2])
308 elif self._gradient != None:
309
310 cs, ce = self._gradient
311 gradient = cairo.LinearGradient(0, 0, 0, rect.height)
312 gradient.add_color_stop_rgb(0, cs[0], cs[1], cs[2])
313 gradient.add_color_stop_rgb(1, ce[0], ce[1], ce[2])
314 context.set_source(gradient)
315 elif self._image_surface:
316 context.set_source_surface(self._image_surface, 0, 0)
317 else:
318 context.set_source_rgb(1, 1, 1)
319
320 context.rectangle(0, 0, rect.width, rect.height)
321 context.fill()
322
324 """
325 The set_color() method can be used to change the color of the
326 background.
327
328 @type color: a color
329 @param color: Set the background to be filles with this color.
330 """
331 self.set_property("color", color)
332 self.set_property("gradient", None)
333 self.set_property("image", "")
334 self.emit("appearance_changed")
335
337 return self.get_property("color")
338
340 """
341 Use set_gradient() to define a vertical gradient as the background.
342
343 @type color_start: a color
344 @param color_start: The starting (top) color of the gradient.
345 @type color_end: a color
346 @param color_end: The ending (bottom) color of the gradient.
347 """
348 self.set_property("color", None)
349 self.set_property("gradient", (color_start, color_end))
350 self.set_property("image", "")
351 self.emit("appearance_changed")
352
354 return self.get_property("gradient")
355
357 """
358 The set_image() method sets the background to be filled with a png
359 image.
360
361 @type filename: string
362 @param filename: Path to the png file you want to use as background
363 image. If the file does not exists, the background is set to white.
364 """
365 try:
366 self._image_surface = cairo.ImageSurface.create_from_png(filename)
367 except:
368 self._image_surface = None
369
370 self.set_property("color", None)
371 self.set_property("gradient", None)
372 self.set_property("image", filename)
373 self.emit("appearance_changed")
374
376 return self.get_property("image")
377
378
379 -class Title(ChartObject):
380 """
381 The title of a chart. The title will be drawn centered at the top of the
382 chart.
383 """
384
385 __gproperties__ = {"color": (gobject.TYPE_PYOBJECT,
386 "title color",
387 "The color of the title in (r,g,b) format. r,g,b in [0,1]",
388 gobject.PARAM_READWRITE),
389 "text": (gobject.TYPE_STRING,
390 "title text",
391 "The text to show as the title.",
392 "", gobject.PARAM_READWRITE)}
393
398
400 if property.name == "visible":
401 return self._show
402 elif property.name == "antialias":
403 return self._antialias
404 elif property.name == "text":
405 return self._text
406 elif property.name == "color":
407 return self._color
408 else:
409 raise AttributeError, "Property %s does not exist." % property.name
410
412 if property.name == "visible":
413 self._show = value
414 elif property.name == "antialias":
415 self._antialias = value
416 elif property.name == "text":
417 self._text = value
418 elif property.name == "color":
419 self._color = value
420 else:
421 raise AttributeError, "Property %s does not exist." % property.name
422
424 """
425 Do all the drawing stuff.
426
427 @type context: cairo.Context
428 @param context: The context to draw on.
429 @type rect: gtk.gdk.Rectangle
430 @param rect: A rectangle representing the charts area.
431 """
432 if self._text != None:
433
434 font = gtk.Label().style.font_desc.get_family()
435 context.set_font_size(rect.height / 30)
436 context.select_font_face(font,cairo.FONT_SLANT_NORMAL, \
437 cairo.FONT_WEIGHT_BOLD)
438 size = context.text_extents(self._text)
439
440 x = (rect.width - size[2]) / 2
441 y = size[3] + rect.height / 80
442
443 c = self._color
444 context.move_to(x, y)
445 context.set_source_rgb(c[0], c[1], c[2])
446 context.show_text(self._text)
447 context.fill()
448
449
450 context.select_font_face(font,cairo.FONT_SLANT_NORMAL, \
451 cairo.FONT_WEIGHT_NORMAL)
452
454 """
455 The set_color() method sets the color of the title text.
456
457 @type color: a color
458 @param color: The color of the title.
459 """
460 self.set_property("color", color)
461 self.emit("appearance_changed")
462
464 return self.get_property("color")
465
466 - def set_text(self, text):
467 """
468 Use the set_text() method to set the title of the chart.
469
470 @type text: string
471 @param text: The title of the chart.
472 """
473 self.set_property("text", text)
474 self.emit("appearance_changed")
475
476 - def get_text(self):
477 return self.get_property("text")
478