Package pygtk_chart :: Module chart
[hide private]
[frames] | no frames]

Source Code for Module pygtk_chart.chart

  1  #!/usr/bin/env python 
  2  # 
  3  #       plot.py 
  4  #        
  5  #       Copyright 2008 Sven Festersen <sven@sven-festersen.de> 
  6  #        
  7  #       This program is free software; you can redistribute it and/or modify 
  8  #       it under the terms of the GNU General Public License as published by 
  9  #       the Free Software Foundation; either version 2 of the License, or 
 10  #       (at your option) any later version. 
 11  #        
 12  #       This program is distributed in the hope that it will be useful, 
 13  #       but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 15  #       GNU General Public License for more details. 
 16  #        
 17  #       You should have received a copy of the GNU General Public License 
 18  #       along with this program; if not, write to the Free Software 
 19  #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
 20  #       MA 02110-1301, USA. 
 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
54 - def __init__(self):
55 gtk.DrawingArea.__init__(self) 56 self.connect("expose_event", self.expose) 57 #objects needed for every chart 58 self.background = Background() 59 self.background.connect("appearance-changed", self._cb_appearance_changed) 60 self.title = Title() 61 self.title.connect("appearance-changed", self._cb_appearance_changed)
62
63 - def _cb_appearance_changed(self, object):
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
87 - def draw_basics(self, context, rect):
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
112 - def export_svg(self, filename):
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
126 - def export_png(self, filename):
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
142 -class ChartObject(gobject.GObject):
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
160 - def __init__(self):
161 gobject.GObject.__init__(self) 162 self._show = True 163 self._antialias = True
164
165 - def do_get_property(self, property):
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
173 - def do_set_property(self, property, value):
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
181 - def _do_draw(self, context, rect):
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
209 - def set_antialias(self, antialias):
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
221 - def get_antialias(self):
222 return self.get_property("antialias")
223
224 - def set_visible(self, visible):
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
235 - def get_visible(self):
236 return self.get_property("visible")
237 238 239 gobject.type_register(ChartObject) 240 241
242 -class Background(ChartObject):
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
260 - def __init__(self):
261 ChartObject.__init__(self) 262 self._color = (1, 1, 1) #the backgound is filled white by default 263 self._gradient = None 264 self._image = "" 265 self._image_surface = None
266
267 - def do_get_property(self, property):
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
281 - def do_set_property(self, property, value):
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
295 - def _do_draw(self, context, rect):
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 #set source color 306 c = self._color 307 context.set_source_rgb(c[0], c[1], c[2]) 308 elif self._gradient != None: 309 #set source gradient 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) #fallback to white bg 319 #create the background rectangle and fill it: 320 context.rectangle(0, 0, rect.width, rect.height) 321 context.fill()
322
323 - def set_color(self, color):
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
336 - def get_color(self):
337 return self.get_property("color")
338
339 - def set_gradient(self, color_start, color_end):
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
353 - def get_gradient(self):
354 return self.get_property("gradient")
355
356 - def set_image(self, filename):
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
375 - def get_image(self):
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
394 - def __init__(self, text=None):
395 ChartObject.__init__(self) 396 self._text = text 397 self._color = (0, 0, 0)
398
399 - def do_get_property(self, property):
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
411 - def do_set_property(self, property, value):
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
423 - def _do_draw(self, context, rect):
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 #prepare the font 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 #calculate position 440 x = (rect.width - size[2]) / 2 441 y = size[3] + rect.height / 80 442 #draw 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 #reset font 450 context.select_font_face(font,cairo.FONT_SLANT_NORMAL, \ 451 cairo.FONT_WEIGHT_NORMAL)
452
453 - def set_color(self, color):
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
463 - def get_color(self):
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