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 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 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]