#!/usr/bin/env python import gtk, gobject from warnings import warn class CellRendererProgressMaskStateWarning(UserWarning): pass class CellRendererProgressMask(gtk.GenericCellRenderer): __gproperties__ = { 'slots': (object, 'slots', 'Mask of filled bins', gobject.PARAM_READWRITE), } def on_get_size(self, widget, cell): return 0, 0, -1, self.props.ypad + 6 def do_get_property(self, property): return getattr(self, '_CellRendererProgressMask__' + property.name) def do_set_property(self, property, value): setattr(self, '_CellRendererProgressMask__' + property.name, value) def set_states(self, states): self.__states = states def on_render(self, window, widget, bg_area, cell, expose, flags): cr = window.cairo_create() cr.rectangle(expose.x, expose.y, expose.width, expose.height) cr.clip() xpad = self.props.xpad xalign = self.props.xalign ypad = self.props.ypad yalign = self.props.yalign cr.translate(cell.x + xpad * xalign, cell.y + ypad * yalign) slots = self.props.slots if not slots: return bins = len(slots) def draw(start, width, cr=cr, a=[1.0]): try: cr.set_source_rgba(*(list(self.__states[start[0]]) + a)[:4]) except (IndexError, KeyError): warn('Invalid State %r in slot %d' % start, CellRendererProgressMaskStateWarning) else: cr.rectangle(start[1], 0, width, 1) cr.fill() cr.save() cr.scale(float(cell.width - xpad) / bins, cell.height - ypad) start = slots[0], 0 for i, val in enumerate(slots): if val != start[0]: # collect like-bins to avoid striping draw(start, i - start[1]) start = val, i else: draw(start, i + 1 - start[1]) def round(x, y, cr=cr): x, y = map(int, cr.user_to_device(x, y)) return cr.device_to_user(x + 0.5, y + 0.5) one_pixel = cr.device_to_user_distance(1, 1) if max(one_pixel) < 0.25: cr.set_line_width(min(one_pixel)) cr.set_source_rgba(0, 0, 0, 0.9) for i in xrange(1, bins): cr.move_to(*round(i, 0)) cr.line_to(*round(i, 1)) cr.stroke() cr.restore() cr.rectangle(0.5, 0.5, cell.width - xpad, cell.height - ypad) if flags & gtk.CELL_RENDERER_PRELIT: cr.set_source_rgba(1, 1, 1, 0.25) cr.fill_preserve() cr.set_source_rgba(0, 0, 0, 0.9) cr.set_line_width(1) cr.stroke() def __main__(): model = gtk.ListStore(int, object) model.append([5, [1, 0, 2, 0, 1]]) model.append([6, [0, 1, 0, 1, 0, 1]]) model.append([7, [1, 0] * 4]) model.append([8, [0, 1] * 4]) model.append([31, [1, 1] * 16]) model.append([63, [1, 1] * 32]) model.append([1023, [1, 1, 1, 0, 2, 2, 2, 0] * 128]) crpm = CellRendererProgressMask() tv = gtk.TreeView(model) #tv.append_column(gtk.TreeViewColumn('bins', gtk.CellRendererText(), text=0)) tv.append_column(gtk.TreeViewColumn('progress', crpm, slots=1)) crpm.set_states([(1, 1, 1, 0.3), (0, 0.5, 0), (0.8, 0, 0)]) #crpm.props.xpad = 10 #crpm.props.xalign = 0.0 crpm.props.ypad = 8 #crpm.props.yalign = 0.5 for column in tv.get_columns(): column.set_resizable(True) win = gtk.Window() win.add(tv) tv.show() win.connect('delete-event', gtk.main_quit) win.present() gtk.main() if __name__ == '__main__': __main__()