# Slider_multi.py
# By MW, Jul 2013
# GPLv3+
#
# Slider containing various information about the cell cycle.
# 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.tmp = 0
self.par = parent
self.features_list = None
self.cell = None # Time limits of the cell
self.params = {'top_r' : 12,
'bottom_r' : 20,
'top_t' : 5,
'bottom_t' : 40,
'to_px' : self.frame2px
}
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.set_size_request(-1, 45)
self.connect("expose-event", self.expose)
[docs] def set_features(self, features_l) :
"""Set a list of gtk features to be drawn on the canvas"""
self.features_list = features_l
[docs] def set_cell(self, c) :
"""Set the cell as an object representing the time extension of the
polygon."""
self.cell = c
[docs] def frame2px(self, frame) :
"""Converts a frame number to a pixel index"""
# Be careful with allocation
width = self.allocation.width
return (width / float(self.size[1]-self.size[0])) * frame
[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
step = round(width / self.ngrad)
"""
(self.cur_width_l, self.cur_width_r) = self.par.get_cur_value()
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, self.params['top_r'], till_r-till_l, self.params['bottom_r'])
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, 7)
cr.line_to(i*step, self.params['top_r'])
cr.stroke()
(x, y, width, height, dx, dy) = cr.text_extents(self.num[i-1])
cr.move_to(i*step-width/2, self.params['top_t'])
cr.text_path(self.num[i-1])
cr.stroke()
for f in self.features_list :
f.draw(cr, self.params)
#cr.save()
#cr.clip()
#cr.paint()
#cr.restore()
[docs]class Slider_multi(gobject.GObject):
def __init__(self, size):
# 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.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_digits(0)
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_digits(0)
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.show_all()
return hbox
[docs] def update(self) :
"""Update the display of the widget"""
self.burning.queue_draw()
"""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
def click_release(self, eb, w) :
self.button_pressed = False
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()
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")
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")
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")
def init_values(self, (left, right)) :
This function can be called before the widget is initiated
self.value_l = left
self.value_r = right
def get_cur_value(self):
return (self.value_l, self.value_r)
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)
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)
def to_px(self, x) :
Returns coordinates in pixels
width = self.burning.allocation.width
return float(x)/self.range[1]*width"""
[docs] def set_features(self, f_l) :
self.burning.set_features(f_l)
# Register new signal
#gobject.type_register(Slider_multi)
#gobject.signal_new("slider-changed", Slider_multi, gobject.SIGNAL_RUN_FIRST,
# gobject.TYPE_NONE, ())
if __name__ == "__main__" :
import Features, Feature_gtk
features = Features.Features()
f1 = features.new_feature('point', 'telophase', 'T', (0,0,1), '')
f2 = features.new_feature('segment', 'anaphase', 'A', (0,2,0), '')
f1_gtk = Feature_gtk.Point_gtk(f1)
f2_gtk = Feature_gtk.Segment_gtk(f2)
w = gtk.Window()
w.set_title("Slider")
w.set_size_request(350, 60)
w.set_position(gtk.WIN_POS_CENTER)
w.connect("destroy", gtk.main_quit)
s = Slider_multi((0,99))
w.add(s.get_panel())
w.show()
s.set_features([f1_gtk, f2_gtk])
gtk.main()