# CellTracking.py
# Adapted by MW, Jun 2013
#+ from ZetCode.com PyGTK tutorial, Jan Bodnar, Apr. 2011
# GPLv3+
#
# Class representing a slider, a new widget created to represent a cell
import gtk, numpy, gobject
import cairo
[docs]class Slider_private(gtk.DrawingArea):
def __init__(self, parent, size):
self.par = parent
super(Slider_private, self).__init__()
self.size = size
# induces problems for small n
tmp = numpy.linspace(size[0], size[1], 7)[1:-1]
if size[1]-size[0] > 50 :
tmp = numpy.int_(tmp)
self.ngrad = float(len(tmp))+1
self.num = []
for n in tmp :
self.num.append(str('%0.1f') % n)
#self.num = ("75","150","225","300","375","450","525","600","675")
self.set_size_request(-1, 35)
self.connect("expose-event", self.expose)
[docs] 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(8)
width = self.allocation.width
(self.cur_width_l, self.cur_width_r) = self.par.get_cur_value()
step = round(width / self.ngrad)
till_l = (width / float(self.size[1]-self.size[0])) * self.cur_width_l
till_r = (width / float(self.size[1]-self.size[0])) * self.cur_width_r
cr.set_source_rgb(1.0, 1.0, 0.72)
cr.rectangle(till_l, 0, till_r-till_l, 35)
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 Slider(gtk.Window):
[docs]class Slider(gobject.GObject):
def __init__(self, size):
#super(Slider, self).__init__()
# Create custom event
self.__gobject_init__()
self.button_pressed = False
self.range = size
self.value_l = size[0]
self.value_r = size[1]
[docs] def get_panel(self) :
"""Returns the slider object"""
#hbox = gtk.HBox()
hbox = gtk.Table(2,3)
(x,y) = self.range
adj_l = gtk.Adjustment(x, x, y, 1, 0)
self.scale_l = gtk.SpinButton(adj_l)#gtk.HScale()
#self.scale_l.set_range(self.range[0], self.range[1])
self.scale_l.set_digits(0)
#self.scale_l.set_size_request(160, 40)
self.scale_l.set_value(self.value_l)
self.scale_l.connect("value-changed", self.on_change_l)
adj_r = gtk.Adjustment(y, x, y, 1, 0)
self.scale_r = gtk.SpinButton(adj_r)#gtk.HScale()
#self.scale_r.set_range(self.range[0],self.range[1])
self.scale_r.set_digits(0)
#self.scale_r.set_size_request(160, 40)
self.scale_r.set_value(self.value_r)
self.scale_r.connect("value-changed", self.on_change_r)
self.burning = Slider_private(self, self.range)
eb = gtk.EventBox()
eb.connect("button-press-event", self.click_press)
eb.connect("button-release-event", self.click_release)
eb.connect('motion-notify-event', self.click_move)
eb.add(self.burning)
hbox.attach(gtk.Label("Begin:"), 0,1,0,1, gtk.SHRINK)
hbox.attach(self.scale_l, 0,1,1,2, gtk.SHRINK)
hbox.attach(eb, 1,2,0,2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL|gtk.EXPAND)
hbox.attach(gtk.Label("End:"), 2,3,0,1, gtk.SHRINK)
hbox.attach(self.scale_r, 2,3,1,2, gtk.SHRINK)
#hbox.pack_start(self.scale_l, expand=False, fill=False)
#hbox.pack_start(eb, expand=True, fill=True)
#hbox.pack_start(self.scale_r, expand=False, fill=False)
hbox.show_all()
return hbox
[docs] def click_press(self, eb, w) :
thr = 4
till_l = self.to_px(self.value_l)
till_r = self.to_px(self.value_r)
if abs(w.x-till_l) < thr :
self.button_pressed = 'left'
elif abs(w.x-till_r) < thr :
self.button_pressed = 'right'
else :
self.button_pressed = None
[docs] def click_release(self, eb, w) :
self.button_pressed = False
[docs] def click_move(self, eb, w) :
if self.value_r - self.value_l >=1:
if self.button_pressed == 'left' :
self.value_l = self.to_coords(w.x)
self.scale_l.set_value(self.value_l)
elif self.button_pressed == 'right' :
self.value_r = self.to_coords(w.x)
self.scale_r.set_value(self.value_r)
else :
self.value_r = self.value_l+1
self.scale_r.set_value(self.value_r)
self.emit("slider-changed")
self.burning.queue_draw()
[docs] def on_change_l(self, widget):
v = widget.get_value()
self.value_l = v
self.no_overlap()
self.burning.queue_draw()
self.emit("slider-changed")
[docs] def on_change_r(self, widget):
v = widget.get_value()
self.value_r = v
self.no_overlap()
self.burning.queue_draw()
self.emit("slider-changed")
[docs] def set_value(self, l_r, value) :
"""Function called to set the value of the slider. Should update
everything
l_r = 'left' | 'right'"""
if l_r == 'left' :
self.value_l = value
self.scale_l.set_value(value)
elif l_r == 'right' :
self.value_r = value
self.scale_r.set_value(value)
self.no_overlap()
self.burning.queue_draw()
self.emit("slider-changed")
[docs] def init_values(self, (left, right)) :
"""This function can be called before the widget is initiated"""
self.value_l = left
self.vlaue_r = right
[docs] def get_cur_value(self):
return (self.value_l, self.value_r)
[docs] def no_overlap(self) :
if self.value_r - self.value_l < 1 :
if self.value_r+1 <= self.range[1] :
self.value_r = self.value_l+1
self.scale_r.set_value(self.value_r)
else :
self.value_l = self.value_r -1
self.scale_l.set_value(self.value_l)
[docs] def to_coords(self, x) :
"""Return coordinates of the click in units (not px)"""
width = self.burning.allocation.width
return int(x*self.range[1]/width)
[docs] def to_px(self, x) :
"""Returns coordinates in pixels"""
width = self.burning.allocation.width
return float(x)/self.range[1]*width
# Register new signal
gobject.type_register(Slider)
gobject.signal_new("slider-changed", Slider, gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ())
if __name__ == "__main__" :
w = gtk.Window()
w.set_title("Slider")
w.set_size_request(350, 30)
w.set_position(gtk.WIN_POS_CENTER)
w.connect("destroy", gtk.main_quit)
s = Slider((0,10))
w.add(s.get_panel())
w.show()
gtk.main()