Show graphs using PyGTK
Although PyGTK is in its final days, I’m still using it on DeTraS development, and I needed a way to generate charts and show them in the GUI. In this post I’ll give you some steps to perform this task easily.
The first step is to find a way to draw charts and graphs. After looking for an easy and lightweight Python module to perform this task, I decided to try with Pycha. It’s really simple to generate a plot using it, so I think you’ll be able to draw a chart just hacking a bit with these examples (included in Pycha’s code).
Now, you should paint a chart in your GUI. PyGTK provides a class to perform drawings on the screen, called DrawingArea. The best way to use this class is creating a subclass and implementing some methods to draw and handle some signals. In my case, I wanted to show several types of plots, so I’ve created an abstract class called Plot, which is a subclass of DrawingArea and it’s the parent of LinePlot and VBarPlot classes, which will hold and paint a line chart and a vertical bar chart respectively.
Once you have a class extending DrawingArea, you need to implement two methods to handle expose_event and size-allocate signals. The expose_event signal will be fired every time your DrawingArea is shown or it has to be redrawn. The size-allocate signal will be fired when the window that holds your DrawingArea is resized.
I’m sure you would rather want to see some code instead of reading more boring explanations about what I’ve done. No problem, this is the code of Plot class:
class Plot (gtk.DrawingArea): def __init__ (self): gtk.DrawingArea.__init__(self) self.connect ("expose_event", self.expose) self.connect ("size-allocate", self.size_allocate) self._surface = None self._options = None def set_options (self, options): """Set plot's options""" self._options = options def set_data (self, data): pass def plot (self): pass def expose (self, widget, event): context = widget.window.cairo_create () context.rectangle (event.area.x, event.area.y, event.area.width, event.area.height) context.clip () self.draw (context) return False def draw (self, context): rect = self.get_allocation() self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \ rect.width, rect.height) self.plot () context.set_source_surface (self._surface, 0, 0) context.paint () def size_allocate (self, widget, requisition): self.queue_draw ()
And these is LinePlot class:
class LinePlot (Plot): def __init__ (self): Plot.__init__ (self) def set_data (self, data): """ Update plot's data and refreshes DrawingArea contents. Data must be a list containing a set of x and y values. """ self._data = (('Data', data),) self.queue_draw () def plot (self): """Initializes chart (if needed), set data and plots.""" chart = LineChart (self._surface, self._options) chart.addDataset (self._data) chart.render ()
If you want to see the complete source file, you can find it here.
Nice hacking 😉
Filed under: Tutorials | Leave a Comment