class SeqTrack(Track): """Track for displaying a single sequence along the genome""" def __init__(self, seqs, **options): options.setdefault("size", [0.0, 0.5]) Track.__init__(self, **options) self.seqs = seqs self.shown = False self.multiscale = Multiscale(marginx=.5, marginy=.5) def draw(self): self.shown = False self.multiscale.init(self.get_window()) # return placeholder group self.gid = group() return self.gid def update(self): if self.view == None: raise Exception("Track view not initialized") win = self.get_window() view = win.get_visible() x, y = self.pos if self.multiscale.atleast(4, .1, view=view): if not self.shown or \ not self.multiscale.same_view(view): self.shown = True start = max(int(self.multiscale.worldx1 - x + self.view.start), int(self.view.start)) end = min(int(self.multiscale.worldx2 - x + self.view.start), int(self.view.end)) seq = self.seqs.getseq(self.view.seqname, start, end) # convert from inclusive to exclusive end = len(seq) + start - 1 self.gid = win.replace_group( self.gid, group( translate( x, y, color(0, 0, 0), scale( 1, self.size[1], text_scale(seq, start - self.view.start, 0, end - self.view.start + 1, 2, "left", "bottom"))))) elif self.shown: self.shown = False self.gid = win.replace_group(self.gid, group())
class SeqTrack (Track): """Track for displaying a single sequence along the genome""" def __init__(self, seqs, **options): options.setdefault("size", [0.0, 0.5]) Track.__init__(self, **options) self.seqs = seqs self.shown = False self.multiscale = Multiscale(marginx=.5, marginy=.5) def draw(self): self.shown = False self.multiscale.init(self.get_window()) # return placeholder group self.gid = group() return self.gid def update(self): if self.view == None: raise Exception("Track view not initialized") win = self.get_window() view = win.get_visible() x, y = self.pos if self.multiscale.atleast(4, .1, view=view): if not self.shown or \ not self.multiscale.same_view(view): self.shown = True start = max(int(self.multiscale.worldx1 - x + self.view.start), int(self.view.start)) end = min(int(self.multiscale.worldx2 - x + self.view.start), int(self.view.end)) seq = self.seqs.getseq(self.view.seqname, start, end) # convert from inclusive to exclusive end = len(seq) + start - 1 self.gid = win.replace_group(self.gid, group(translate(x, y, color(0,0,0), scale(1, self.size[1], text_scale(seq, start-self.view.start, 0, end-self.view.start+1, 2, "left", "bottom"))))) elif self.shown: self.shown = False self.gid = win.replace_group(self.gid, group())
class CurveTrack (Track): """Track for displaying a curve along the genome""" def __init__(self, xdata, ydata, **options): Track.__init__(self, **options) self.xdata = xdata self.ydata = ydata self.multiscale = Multiscale(marginx=.25, marginy=.25, scalex=4.0, scaley=4.0) self.shown = False def draw(self): self.multiscale.init(self.get_window()) self.shown = False # return placeholder group self.gid = group() return self.gid def update(self): assert self.view != None, "Track view not initialized" win = self.get_window() if not self.shown or not self.multiscale.same_view(): self.shown = True x, y = self.pos start2 = self.view.start start = int(max(0, start2-1, self.multiscale.worldx1-x+start2)) end = int(min(len(self.xdata), self.view.end, self.multiscale.worldx2-x+start2)) step = max(1, (end - start) // 400) vis = [] vis2 = [] for i in xrange(start, end, step): dat = self.ydata[i:i+step] assert len(dat) > 0, (start, end, step) y1 = min(dat) y2 = max(dat) #y = self.ydata[i] y1 = (util.clamp(y1, .33, .66) - .33) / .33 y2 = (util.clamp(y2, .33, .66) - .33) / .33 vis.extend([self.xdata[i], y2 * self.size[1]]) vis2.extend([self.xdata[i], y1 * self.size[1]]) # draw curve on middle of base (.5) self.gid = win.replace_group(self.gid, group(translate(x - self.view.start + .5, y, color(0,1,0), line_strip(* vis), line_strip(* vis2), color(0,0,0), lines(self.view.start - 0.5, 0, self.view.end + 0.5, 0, self.view.start - 0.5, self.size[1], self.view.end + 0.5, self.size[1]))))
class RulerTrack (Track): """Track for displaying a base-pair ruler along the genome""" def __init__(self, top=2.0, bottom=0.0, minicolor=color(.8,.8,.8), maincolor = color(0,0,0), align=None, text_align="middle", text_color=color(0, 0, 0), show=True, fixed_height=True, **options): Track.__init__(self, **options) self.top = top self.bottom = bottom self.minicolor = minicolor self.maincolor = maincolor self.text_align = text_align self.text_color = text_color self.show = show self.shown = show self.fixed_height = fixed_height if align != None: self.coords = alignlib.CoordConverter(align) else: self.coords = None self.multiscale = Multiscale(marginx=.5, marginy=.5, scalex=10, scaley=10) def draw(self): self.multiscale.init(self.get_window()) self.start = self.view.start-1 self.end = self.view.end self.height = self.top self.multiscale.reset() self.gid = group() return group(self.gid) def update(self): self.win = self.get_window() if not self.show: if self.shown: self.gid = self.win.replace_group(self.gid, group()) self.shown = False else: self.shown = True if not self.multiscale.same_view(): if self.coords == None: g = self.draw_ruler(self.pos, self.start, self.end) else: g = self.draw_align_ruler(self.pos, self.start, self.end) self.gid = self.win.replace_group(self.gid, g) def set_visible(self, visible): self.show = visible def draw_ruler(self, pos, start, end): worldx1, worldy1, worldx2, worldy2 = self.win.get_visible() screenwidth, screenheight = self.win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = visual.getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = visual.getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes if unit >= 10: vis.append(self.minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(lines(x + i - start, y+self.bottom, x + i - start, y+self.top)) i += unit // 10 # make main hashes i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(self.maincolor) vis.append(lines(x + i - start, y, x + i - start, y + self.top)) vis.append(self.text_color) vis.append(text(str(int(i//unit2)) + unitstr, x + i - start, y, x + i -start - unit, y + self.top, self.text_align, "right")) i += unit # base line vis.append(lines(self.maincolor, x, y, x + end - start, y)) return group(* vis) def draw_align_ruler(self, pos, start, end): worldx1, worldy1, worldx2, worldy2 = self.win.get_visible() screenwidth, screenheight = self.win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = visual.getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = visual.getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes vis.append(self.minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(lines(x + i - start, y + self.bottom, x + i - start, y + self.height)) i += max(unit // 10, 1) # make main hashes # find starting local coord seqi = unit * ((start + self.coords.align2local(max(0, worldx1 - x), clamp=True)) // unit) \ - start-1 # find starting align coord i = self.coords.local2align(seqi) endseqi = min(self.coords.align2local(end, clamp=True), self.coords.align2local(worldx2-x, clamp=True)) # draw all hashes in view while seqi <= endseqi: vis.append(self.maincolor) vis.append(lines(x + i+1, y, x + i+1, y + self.height)) vis.append(self.text_color) vis.append(text(str(int((seqi+start+1)//unit2)) + unitstr, x + i+1, y, x + i+1 - unit, y + self.height, self.text_align, "right")) seqi += unit i = self.coords.local2align(seqi, clamp=True) # base line vis.append(lines(self.maincolor, x, y, x + end - start, y)) return group(* vis)
class AlignTrack (Track): def __init__(self, aln, collapse=None, cols=None, color_bases=False, seqtype=None, show_color_bases=True, show_bases=True, show_labels=True, rowspacing=None, **options): Track.__init__(self, **options) self.size = [aln.alignlen(), len(aln)] self.multiscale = Multiscale(marginx=.5, marginy=.5) self.collapse = collapse self.show_color_bases = show_color_bases self.show_bases = show_bases self.show_labels = show_labels self.rowspacing = rowspacing self.always_color = False self.color_bases_vis = None if seqtype == None: self.seqtype = guessAlign(aln) else: self.seqtype = seqtype if color_bases == True: if self.seqtype == "dna": self.color_bases = dna_colors elif self.seqtype == "pep": self.color_bases = pep_colors else: self.color_bases = color_bases if collapse != None: assert collapse in aln.keys() cols = util.findneq('-', aln[collapse]) if cols != None: self.aln = alignlib.subalign(aln, cols) else: self.aln = aln def draw(self): self.text_shown = False self.multiscale.init(self.get_window()) BASE = 0 GAP = 1 NOBASE = 2 if self.seqtype == "dna": baseclasses = {'A': BASE, 'C': BASE, 'T': BASE, 'G': BASE, '-': GAP, 'N': NOBASE, '*': NOBASE, 'X': NOBASE} elif self.seqtype == "pep": baseclasses = {'-': GAP, 'N': NOBASE, '*': NOBASE, 'X': NOBASE} for aa in 'ARNDCEQGHILKMFPSTWYVU': baseclasses[aa] = BASE else: raise Exception("unknown seqtype '%s'" % self.seqtype) # init row spacing if self.rowspacing == None: self.rowspacing = range(len(self.aln)) def getRegions(selectedClass): boxpts = [] diagpts = [] diagpts2 = [] for row, (key, val) in zip(self.rowspacing, self.aln.iteritems()): lastbase = None lastclass = None lasti = 0 for i in xrange(len(val)+1): # this extra is being used to handle the case when # a sequence is all bases if i < len(val): base = val[i] else: base = '-' if base not in baseclasses: baseclass = NOBASE else: baseclass = baseclasses[base] if baseclass == lastclass: continue if lastbase != None and lastclass == selectedClass: boxpts.extend([lasti, -row, lasti, -row-1, i, -row-1, i, -row]) diagpts.extend([i, -row, i, -row-1]) #diagpts.extend([lasti, -row, i, -row-1]) #diagpts2.extend([lasti, -row, lasti, -row-1]) lasti = i lastbase = base lastclass = baseclass return boxpts, diagpts, diagpts2 base_boxpts, base_diagpts, base_diagpts2 = getRegions(BASE) nobase_boxpts, nobase_diagpts, nobase_diagpts2 = getRegions(NOBASE) # build labels if self.show_labels: labels = [] for i, key in zip(self.rowspacing, self.aln): labels.append(text_clip(key, -util.INF, -i, 0, -i-1, 4, 12, "middle", "right")) labelsgroup = group(*labels) else: labelsgroup = group() # build hotspot click = hotspot("click", 0, 0, self.aln.alignlen(), -self.size[1], self.on_click_callback) self.text_group = group() return group(translate(self.pos[0], self.pos[1] + self.size[1], color(0, 0, 0), labelsgroup, click, color(.5, .5, .5), quads(* base_boxpts), lines(* base_diagpts), #lines(* base_diagpts2), color(.7, .2, .2), quads(* nobase_boxpts), lines(* nobase_diagpts), #lines(* nobase_diagpts2), group(self.text_group))) def draw_left(self): labels = [] maxsize = max(map(len, self.aln.keys())) for i, key in enumerate(self.aln): labels.append(text_clip(key, -util.INF, -i, 0, -i-1, 4, 12, "middle", "right")) return group(translate(self.pos[0], self.pos[1] + self.size[1], color(0,0,0), #lines(0, 0, 0, -len(self.aln), # -maxsize, 0, 0, 0, # -maxsize, -len(self.aln), 0, -len(self.aln)), *labels)) def update(self): win = self.get_window() view = win.get_visible() size = win.get_size() x, y = self.pos mintextSize = 4 minblockSize = 1 color_bases = self.color_bases if self.multiscale.atleast(minblockSize, .1, view=view, size=size): if not self.text_shown or \ not self.multiscale.same_view(view): self.text_shown = True start = max(int(self.multiscale.worldx1 - x - 1), 0) end = max(int(self.multiscale.worldx2 - x), start) vis = [] vis2 = [] for i, row in enumerate(self.aln.itervalues()): seq = row[start:end] seq = seq.replace("-", " ") # color bases if self.show_color_bases and color_bases != False: for j in xrange(len(seq)): base = seq[j].upper() if base in color_bases: vis2.extend([color_bases[base], quads(start + j, -i, start + j, -i-1, start + j+1, -i-1, start + j+1, -i)]) end2 = start + len(seq) # draw text if self.show_bases and \ self.multiscale.atleast(mintextSize, 2, view=view, size=size): vis.append(text_scale(seq, start, -i+1, end2, -i-1, "left", "bottom")) self.text_group = win.replace_group(self.text_group, group(group(*vis2), color(0,0,0), * vis)) elif self.text_shown: self.text_shown = False self.text_group = win.replace_group(self.text_group, group()) def on_click_callback(self): # TODO: should not rely on get_mouse_pos("world") win = self.get_window() x, y = win.get_mouse_pos('world') x -= self.pos[0] y = self.size[1] - (y - self.pos[1]) self.on_click(x, y) def on_click(self, x, y): y = int(y) if 0 <= y < len(self.aln): print self.aln.keys()[y]
class Ruler(summon.VisObject): """ Ruler visualization object """ def __init__(self, win, start, end, height=20, bottom=0, unitstr="", minicolor=color(.8, .8, .8), maincolor=color(0, 0, 0), pos=[0.0, 0.0]): summon.VisObject.__init__(self) self.win = win self.start = start self.end = end self.height = height self.bottom = bottom self.pos = pos[:] self.minicolor = minicolor self.maincolor = maincolor self.multiscale = Multiscale(marginx=.5, marginy=.5, scalex=10, scaley=10) def draw(self): self.multiscale.init(self.get_window()) g = group() self.gid = get_group_id(g) return g def update(self): if not self.multiscale.same_view(): g = draw_ruler(self.win, self.pos, self.start, self.end, height=self.height, bottom=self.bottom, minicolor=self.minicolor, maincolor=self.maincolor, win=self.win) self.gid = self.win.replace_group(self.gid, g) def draw_ruler(self, pos, start, end, height=20, bottom=0, unit=None, unitstr="", minicolor=color(.8, .8, .8), maincolor=color(0, 0, 0)): win = self.win worldx1, worldy1, worldx2, worldy2 = win.get_visible() screenwidth, screenheight = win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes if unit >= 10: vis.append(minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append( lines(x + i - start, y + bottom, x + i - start, y + height)) i += unit // 10 # make main hashes vis.append(maincolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(lines(x + i - start, y, x + i - start, y + height)) vis.append( text( str(int(i // unit2)) + unitstr, x + i - start, y, x + i - start - unit, y + height, "middle", "right")) i += unit # base line vis.append(lines(color(0, 0, 0), x, y, x + end - start, y)) return group(*vis) drawRuler = draw_ruler
class Ruler (summon.VisObject): """ Ruler visualization object """ def __init__(self, win, start, end, height=20, bottom=0, unitstr="", minicolor=color(.8, .8, .8), maincolor=color(0, 0, 0), pos=[0.0, 0.0]): summon.VisObject.__init__(self) self.win = win self.start = start self.end = end self.height = height self.bottom = bottom self.pos = pos[:] self.minicolor = minicolor self.maincolor = maincolor self.multiscale = Multiscale( marginx=.5, marginy=.5, scalex=10, scaley=10) def draw(self): self.multiscale.init(self.get_window()) g = group() self.gid = get_group_id(g) return g def update(self): if not self.multiscale.same_view(): g = draw_ruler(self.win, self.pos, self.start, self.end, height=self.height, bottom=self.bottom, minicolor=self.minicolor, maincolor=self.maincolor, win=self.win) self.gid = self.win.replace_group(self.gid, g) def draw_ruler(self, pos, start, end, height=20, bottom=0, unit=None, unitstr="", minicolor=color(.8, .8, .8), maincolor=color(0, 0, 0)): win = self.win worldx1, worldy1, worldx2, worldy2 = win.get_visible() screenwidth, screenheight = win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes if unit >= 10: vis.append(minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(lines(x + i - start, y + bottom, x + i - start, y + height)) i += unit // 10 # make main hashes vis.append(maincolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(lines(x + i - start, y, x + i - start, y + height)) vis.append(text(str(int(i//unit2)) + unitstr, x + i - start, y, x + i - start - unit, y + height, "middle", "right")) i += unit # base line vis.append(lines(color(0, 0, 0), x, y, x + end - start, y)) return group(* vis) drawRuler = draw_ruler
class CurveTrack(Track): """Track for displaying a curve along the genome""" def __init__(self, xdata, ydata, **options): Track.__init__(self, **options) self.xdata = xdata self.ydata = ydata self.multiscale = Multiscale(marginx=.25, marginy=.25, scalex=4.0, scaley=4.0) self.shown = False def draw(self): self.multiscale.init(self.get_window()) self.shown = False # return placeholder group self.gid = group() return self.gid def update(self): assert self.view != None, "Track view not initialized" win = self.get_window() if not self.shown or not self.multiscale.same_view(): self.shown = True x, y = self.pos start2 = self.view.start start = int( max(0, start2 - 1, self.multiscale.worldx1 - x + start2)) end = int( min(len(self.xdata), self.view.end, self.multiscale.worldx2 - x + start2)) step = max(1, (end - start) // 400) vis = [] vis2 = [] for i in xrange(start, end, step): dat = self.ydata[i:i + step] assert len(dat) > 0, (start, end, step) y1 = min(dat) y2 = max(dat) #y = self.ydata[i] y1 = (util.clamp(y1, .33, .66) - .33) / .33 y2 = (util.clamp(y2, .33, .66) - .33) / .33 vis.extend([self.xdata[i], y2 * self.size[1]]) vis2.extend([self.xdata[i], y1 * self.size[1]]) # draw curve on middle of base (.5) self.gid = win.replace_group( self.gid, group( translate( x - self.view.start + .5, y, color(0, 1, 0), line_strip(*vis), line_strip(*vis2), color(0, 0, 0), lines(self.view.start - 0.5, 0, self.view.end + 0.5, 0, self.view.start - 0.5, self.size[1], self.view.end + 0.5, self.size[1]))))
class RulerTrack(Track): """Track for displaying a base-pair ruler along the genome""" def __init__(self, top=2.0, bottom=0.0, minicolor=color(.8, .8, .8), maincolor=color(0, 0, 0), align=None, text_align="middle", text_color=color(0, 0, 0), show=True, fixed_height=True, **options): Track.__init__(self, **options) self.top = top self.bottom = bottom self.minicolor = minicolor self.maincolor = maincolor self.text_align = text_align self.text_color = text_color self.show = show self.shown = show self.fixed_height = fixed_height if align != None: self.coords = alignlib.CoordConverter(align) else: self.coords = None self.multiscale = Multiscale(marginx=.5, marginy=.5, scalex=10, scaley=10) def draw(self): self.multiscale.init(self.get_window()) self.start = self.view.start - 1 self.end = self.view.end self.height = self.top self.multiscale.reset() self.gid = group() return group(self.gid) def update(self): self.win = self.get_window() if not self.show: if self.shown: self.gid = self.win.replace_group(self.gid, group()) self.shown = False else: self.shown = True if not self.multiscale.same_view(): if self.coords == None: g = self.draw_ruler(self.pos, self.start, self.end) else: g = self.draw_align_ruler(self.pos, self.start, self.end) self.gid = self.win.replace_group(self.gid, g) def set_visible(self, visible): self.show = visible def draw_ruler(self, pos, start, end): worldx1, worldy1, worldx2, worldy2 = self.win.get_visible() screenwidth, screenheight = self.win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = visual.getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = visual.getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes if unit >= 10: vis.append(self.minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append( lines(x + i - start, y + self.bottom, x + i - start, y + self.top)) i += unit // 10 # make main hashes i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append(self.maincolor) vis.append(lines(x + i - start, y, x + i - start, y + self.top)) vis.append(self.text_color) vis.append( text( str(int(i // unit2)) + unitstr, x + i - start, y, x + i - start - unit, y + self.top, self.text_align, "right")) i += unit # base line vis.append(lines(self.maincolor, x, y, x + end - start, y)) return group(*vis) def draw_align_ruler(self, pos, start, end): worldx1, worldy1, worldx2, worldy2 = self.win.get_visible() screenwidth, screenheight = self.win.get_size() worldwidth = worldx2 - worldx1 worldx1 -= worldwidth / 2.0 worldx2 += worldwidth / 2.0 # find appropriate unit if one is not given unit = visual.getRulerAutoSize(screenwidth, worldwidth) order = int(math.log10(unit)) unit2, unitstr = visual.getUnitSuffix(unit) x, y = pos vis = [] # make mini hashes vis.append(self.minicolor) i = unit * (max(start, worldx1 - x + start) // unit) while x + i - start <= worldx2 and i < end: if i >= start: vis.append( lines(x + i - start, y + self.bottom, x + i - start, y + self.height)) i += max(unit // 10, 1) # make main hashes # find starting local coord seqi = unit * ((start + self.coords.align2local(max(0, worldx1 - x), clamp=True)) // unit) \ - start-1 # find starting align coord i = self.coords.local2align(seqi) endseqi = min(self.coords.align2local(end, clamp=True), self.coords.align2local(worldx2 - x, clamp=True)) # draw all hashes in view while seqi <= endseqi: vis.append(self.maincolor) vis.append(lines(x + i + 1, y, x + i + 1, y + self.height)) vis.append(self.text_color) vis.append( text( str(int((seqi + start + 1) // unit2)) + unitstr, x + i + 1, y, x + i + 1 - unit, y + self.height, self.text_align, "right")) seqi += unit i = self.coords.local2align(seqi, clamp=True) # base line vis.append(lines(self.maincolor, x, y, x + end - start, y)) return group(*vis)
class AlignTrack(Track): def __init__(self, aln, collapse=None, cols=None, color_bases=False, seqtype=None, show_color_bases=True, show_bases=True, show_labels=True, rowspacing=None, **options): Track.__init__(self, **options) self.size = [aln.alignlen(), len(aln)] self.multiscale = Multiscale(marginx=.5, marginy=.5) self.collapse = collapse self.show_color_bases = show_color_bases self.show_bases = show_bases self.show_labels = show_labels self.rowspacing = rowspacing self.always_color = False self.color_bases_vis = None if seqtype == None: self.seqtype = guessAlign(aln) else: self.seqtype = seqtype if color_bases == True: if self.seqtype == "dna": self.color_bases = dna_colors elif self.seqtype == "pep": self.color_bases = pep_colors else: self.color_bases = color_bases if collapse != None: assert collapse in aln.keys() cols = util.findneq('-', aln[collapse]) if cols != None: self.aln = alignlib.subalign(aln, cols) else: self.aln = aln def draw(self): self.text_shown = False self.multiscale.init(self.get_window()) BASE = 0 GAP = 1 NOBASE = 2 if self.seqtype == "dna": baseclasses = { 'A': BASE, 'C': BASE, 'T': BASE, 'G': BASE, '-': GAP, 'N': NOBASE, '*': NOBASE, 'X': NOBASE } elif self.seqtype == "pep": baseclasses = {'-': GAP, 'N': NOBASE, '*': NOBASE, 'X': NOBASE} for aa in 'ARNDCEQGHILKMFPSTWYVU': baseclasses[aa] = BASE else: raise Exception("unknown seqtype '%s'" % self.seqtype) # init row spacing if self.rowspacing == None: self.rowspacing = range(len(self.aln)) def getRegions(selectedClass): boxpts = [] diagpts = [] diagpts2 = [] for row, (key, val) in zip(self.rowspacing, self.aln.iteritems()): lastbase = None lastclass = None lasti = 0 for i in xrange(len(val) + 1): # this extra is being used to handle the case when # a sequence is all bases if i < len(val): base = val[i] else: base = '-' if base not in baseclasses: baseclass = NOBASE else: baseclass = baseclasses[base] if baseclass == lastclass: continue if lastbase != None and lastclass == selectedClass: boxpts.extend([ lasti, -row, lasti, -row - 1, i, -row - 1, i, -row ]) diagpts.extend([i, -row, i, -row - 1]) #diagpts.extend([lasti, -row, i, -row-1]) #diagpts2.extend([lasti, -row, lasti, -row-1]) lasti = i lastbase = base lastclass = baseclass return boxpts, diagpts, diagpts2 base_boxpts, base_diagpts, base_diagpts2 = getRegions(BASE) nobase_boxpts, nobase_diagpts, nobase_diagpts2 = getRegions(NOBASE) # build labels if self.show_labels: labels = [] for i, key in zip(self.rowspacing, self.aln): labels.append( text_clip(key, -util.INF, -i, 0, -i - 1, 4, 12, "middle", "right")) labelsgroup = group(*labels) else: labelsgroup = group() # build hotspot click = hotspot("click", 0, 0, self.aln.alignlen(), -self.size[1], self.on_click_callback) self.text_group = group() return group( translate( self.pos[0], self.pos[1] + self.size[1], color(0, 0, 0), labelsgroup, click, color(.5, .5, .5), quads(*base_boxpts), lines(*base_diagpts), #lines(* base_diagpts2), color(.7, .2, .2), quads(*nobase_boxpts), lines(*nobase_diagpts), #lines(* nobase_diagpts2), group(self.text_group))) def draw_left(self): labels = [] maxsize = max(map(len, self.aln.keys())) for i, key in enumerate(self.aln): labels.append( text_clip(key, -util.INF, -i, 0, -i - 1, 4, 12, "middle", "right")) return group( translate( self.pos[0], self.pos[1] + self.size[1], color(0, 0, 0), #lines(0, 0, 0, -len(self.aln), # -maxsize, 0, 0, 0, # -maxsize, -len(self.aln), 0, -len(self.aln)), *labels)) def update(self): win = self.get_window() view = win.get_visible() size = win.get_size() x, y = self.pos mintextSize = 4 minblockSize = 1 color_bases = self.color_bases if self.multiscale.atleast(minblockSize, .1, view=view, size=size): if not self.text_shown or \ not self.multiscale.same_view(view): self.text_shown = True start = max(int(self.multiscale.worldx1 - x - 1), 0) end = max(int(self.multiscale.worldx2 - x), start) vis = [] vis2 = [] for i, row in enumerate(self.aln.itervalues()): seq = row[start:end] seq = seq.replace("-", " ") # color bases if self.show_color_bases and color_bases != False: for j in xrange(len(seq)): base = seq[j].upper() if base in color_bases: vis2.extend([ color_bases[base], quads(start + j, -i, start + j, -i - 1, start + j + 1, -i - 1, start + j + 1, -i) ]) end2 = start + len(seq) # draw text if self.show_bases and \ self.multiscale.atleast(mintextSize, 2, view=view, size=size): vis.append( text_scale(seq, start, -i + 1, end2, -i - 1, "left", "bottom")) self.text_group = win.replace_group( self.text_group, group(group(*vis2), color(0, 0, 0), *vis)) elif self.text_shown: self.text_shown = False self.text_group = win.replace_group(self.text_group, group()) def on_click_callback(self): # TODO: should not rely on get_mouse_pos("world") win = self.get_window() x, y = win.get_mouse_pos('world') x -= self.pos[0] y = self.size[1] - (y - self.pos[1]) self.on_click(x, y) def on_click(self, x, y): y = int(y) if 0 <= y < len(self.aln): print self.aln.keys()[y]