Back to index

python-biopython  1.60
xbb_widget.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Created: Wed Jun 21 10:28:14 2000
00003 # Last changed: Time-stamp: <01/09/04 09:42:06 thomas>
00004 # thomas@cbs.dtu.dk, http://www.cbs.dtu.dk/thomas
00005 # File: xbb_widget.py
00006 
00007 # Copyright 2000 by Thomas Sicheritz-Ponten.  All rights reserved.
00008 # This code is part of the Biopython distribution and governed by its
00009 # license.  Please see the LICENSE file that should have been included
00010 # as part of this package.
00011 
00012 import re
00013 import sys
00014 import time
00015 
00016 from Tkinter import *
00017 from tkFileDialog import askopenfilename, asksaveasfilename
00018 
00019 sys.path.insert(0, '.')
00020 from xbb_utils import *
00021 from xbb_translations import xbb_translations
00022 from xbb_blast import BlastIt
00023 from xbb_search import XDNAsearch
00024 from xbb_help import xbbtools_help
00025 from Bio.Data import CodonTable
00026 from Bio.SeqUtils import quick_FASTA_reader
00027 
00028 
00029 class xbb_widget:
00030     def __init__(self, parent = None):
00031         self.is_a_master = (parent == None)
00032         self.parent = parent
00033             
00034         self.init_variables()
00035         self.init_colors()
00036         # master frame
00037         self.main_frame = Frame(parent)
00038         if not parent:
00039             self.init_optionsdb()
00040             self.parent = self.main_frame.master
00041             
00042         self.main_frame.pack(fill = BOTH, expand = 1)
00043         
00044         # sequence info (GC%, positins etc.)
00045         self.info_frame = Frame(self.main_frame)
00046         self.info_frame.pack(fill = BOTH, expand = 1)
00047         
00048         self.create_menu(self.info_frame)
00049         self.create_seqinfo(self.info_frame)
00050 
00051         # sequence field and fast buttons
00052         self.seq_frame = Frame(self.main_frame)
00053         self.seq_frame.pack(fill = BOTH, expand = 1)
00054         
00055         self.create_buttons(self.seq_frame)
00056         self.create_seqfield(self.seq_frame)
00057 
00058         self.create_bindings()
00059         self.blastit = 'xbb_blast.py'
00060         
00061     def init_variables(self):
00062         self.seqwidth = 60
00063         self.translation_tables = {}
00064         for i, table in CodonTable.unambiguous_dna_by_id.iteritems():
00065             self.translation_tables[table.names[0]] = i
00066         self.translator = xbb_translations()
00067 
00068     def init_colors(self):
00069         self.colorsbg = {'frame':'brown',
00070                          'canvas':'brown',
00071                          'button':'darkgreen',
00072                          'radiobutton':'darkgrey',
00073                          'checkbutton':'darkgrey',
00074                          'label':'dimgrey',
00075                          'text':'bisque1',
00076                          'entry':'bisque1',
00077                          'menu':'darkgreen',
00078                          'menubutton':'darkgreen',
00079                          'seqinfo':'dimgrey'}
00080         self.colorsfg = {'button':'lightblue',
00081                          'radiobutton':'lightblue',
00082                          'checkbutton':'lightblue',
00083                          'label':'green3',
00084                          'text':'black',
00085                          'entry':'black',
00086                          'menu':'black',
00087                          'menubutton':'lightblue'}
00088         self.colorsNT = {'A':'green',
00089                          'C':'lightblue',
00090                          'G':'orange',
00091                          'T':'tomato'
00092                          }
00093 
00094         self.colorsPMWbg = {
00095             'ComboBox':'darkgreen',
00096             'ComboBox.Label':'darkgreen',
00097             }
00098         
00099         self.colorsPMWfg = {
00100             'ComboBox.Label':'lightblue',
00101             }
00102 
00103 
00104     def init_optionsdb(self):
00105         # does anybody know a better way of defining colors ?
00106         # how would one implement Tk's -class ?
00107         tk = self.main_frame.master
00108         for k,v in self.colorsbg.items():
00109             name = '*' + k[0].upper() + k[1:] + '.background'
00110             tk.option_add(name, v)
00111 
00112         for k,v in self.colorsfg.items():
00113             name = '*' + k[0].upper() + k[1:] + '.foreground'
00114             tk.option_add(name, v)
00115             
00116         for k,v in self.colorsPMWbg.items():
00117             name = '*' + k[0].upper() + k[1:] + '.background'
00118             tk.option_add(name, v)
00119 
00120         for k,v in self.colorsPMWfg.items():
00121             name = '*' + k[0].upper() + k[1:] + '.foreground'
00122             tk.option_add(name, v)
00123             
00124     def create_menu(self, parent):
00125         self.menubar = Menu(self.main_frame)
00126         
00127         # File menu
00128         self.file_menu = Menu(self.menubar)
00129         menu = self.file_menu
00130         menu.add_command(label='Exit', command = self.exit)
00131         self.menubar.add_cascade(label="File", menu=self.file_menu)
00132 
00133         # Edit menu
00134         self.edit_menu = Menu(self.menubar)
00135         menu = self.edit_menu
00136         menu.add_command(label='Complement', command = self.complement)
00137         menu.add_command(label='Antiparallel', command = self.antiparallel)
00138         menu.add_command(label='Reverse', command = self.reverse)
00139         menu.add_command(label='Fix sequence', command = self.fix_sequence)
00140         menu.add_command(label='Search', command = self.search)
00141         self.menubar.add_cascade(label="Edit", menu=self.edit_menu)
00142 
00143         # Translation menu
00144         self.translation_menu = Menu(self.menubar)
00145         menu = self.translation_menu
00146         menu.add_command(label='+1 Frame', command = self.translate)
00147         menu.add_command(label='6 Frames', command = self.gcframe)
00148         menu.add_command(label='Extract to FASTA', command = self.extract)
00149 
00150         self.current_codon_table = StringVar()
00151         self.current_codon_table.set('Standard')
00152         self.current_codon_table_id = 1
00153         
00154         keys = self.translation_tables.keys()
00155         keys.remove('Standard')
00156         keys.sort()
00157         keys = ['Standard'] + keys
00158 
00159         self.gencode_menu = Menu(self.translation_menu)
00160         menu = self.gencode_menu
00161         for table in keys:
00162             menu.add_radiobutton(label=table, command = self.set_codon_table, variable = self.current_codon_table)
00163         self.translation_menu.add_cascade(label="Genetic Codes", menu=self.gencode_menu)
00164 
00165 
00166         self.menubar.add_cascade(label="Translations", menu=self.translation_menu)
00167 
00168         # Tools menu
00169         self.tools_menu = Menu(self.menubar)
00170         menu = self.tools_menu
00171         menu.add_command(label='Blast', command = self.blast)
00172         menu.add_command(label='Stats', command = self.statistics)
00173         self.menubar.add_cascade(label="Tools", menu=self.tools_menu)
00174         
00175         # Help menu
00176         self.help_menu = Menu(self.menubar, name = 'help')
00177         menu = self.help_menu
00178         menu.add_command(label='Help', command = xbbtools_help)
00179         self.menubar.add_cascade(label="Help", menu=self.help_menu)
00180 
00181         self.parent.config(menu = self.menubar)
00182 
00183     def set_codon_table(self):
00184         self.current_codon_table_id = self.translation_tables[self.current_codon_table.get()]
00185         
00186     def exit(self, *args):
00187         # depending on if this widget is the first created or a child widget
00188         if self.is_a_master:
00189             sys.exit(0)
00190         else:
00191             self.main_frame.destroy()
00192             
00193     def create_seqinfo(self, parent):
00194         # all the sequence information in the top labels
00195         self.seq_info1 = Frame(parent, relief = RIDGE,
00196                                borderwidth = 5, height = 30)
00197         self.seq_info1.pack(fill = BOTH, expand = 1, side = TOP)
00198 
00199         self.position_ids = {}
00200         d = self.position_ids
00201         d['id'] = Label(self.seq_info1, width = 10)
00202         d['from_id'] = Label(self.seq_info1, width = 10)
00203         d['to_id'] = Label(self.seq_info1, width = 10)
00204         d['length_id'] = Label(self.seq_info1, width = 10)
00205         d['label'] = Label(self.seq_info1, width = 10)
00206         for i in ['id', 'from_id', 'to_id', 'length_id', 'label']:
00207             d[i].pack(side = LEFT, fill = BOTH, expand = 1)
00208 
00209 
00210         
00211         self.seq_info2 = Frame(parent, relief = RIDGE,
00212                                borderwidth = 5, height = 30)
00213         self.seq_info2.pack(fill = BOTH, expand = 1, side = TOP)
00214         self.statistics_ids = {}
00215         d = self.statistics_ids
00216         d['length_id'] = Label(self.seq_info2, width = 10)
00217         d['length_id'].pack(side = LEFT, fill = BOTH, expand = 1)
00218         for nt in ['A','C','G','T']:
00219             d[nt] = Label(self.seq_info2, width = 10, fg = self.colorsNT[nt])
00220             d[nt].pack(side = LEFT, fill = BOTH, expand = 1)
00221             
00222 
00223         
00224     def create_buttons(self, parent):
00225         self.button_frame = Frame(parent)
00226         self.button_frame.pack(fill = Y, side = LEFT)
00227         self.buttons = {}
00228         for text, func in [('Open', self.open),
00229                            ('Export', self.export),
00230                            ('GC Frame', self.gcframe),
00231                            ('Blast', self.blast),
00232                            ('Exit', self.exit)]:
00233             b_id = Button(self.button_frame, text = text,
00234                           command = func, width = 7)
00235             b_id.pack(side = TOP, pady = 5, padx = 10)
00236             self.buttons[text] = b_id
00237 
00238         f = Frame(self.button_frame)
00239         l = Label(f, text = 'Goto:', bg = self.colorsbg['frame'], fg = self.colorsfg['button'])
00240         l.pack(side = LEFT)
00241         l.bind('<Button-1>', self.goto)
00242         
00243         self.goto_entry = Entry(f, width = 5)
00244         self.goto_entry.pack(side = RIGHT, pady = 5, padx = 4)
00245         self.goto_entry.bind('<Return>', self.goto)
00246         f.pack(side = BOTTOM)
00247         
00248     def create_seqfield(self, parent):
00249         self.sequence_id = Text(parent, wrap = 'char',
00250                                 width = self.seqwidth)
00251         self.sequence_id.pack(fill = BOTH, expand = 1, side = RIGHT)
00252 
00253     def create_bindings(self):
00254         self.sequence_id.bind('<Motion>', self.position)
00255         self.sequence_id.bind('<Leave>', lambda x,s = self:
00256                               s.position_ids['id'].configure(text = ''))
00257         self.sequence_id.bind('<1>', self.zero)
00258         self.sequence_id.bind('<B1-Motion>', self.count_selection)
00259         self.sequence_id.bind('<Double-Button-1>', self.select_all)
00260         
00261     def zero(self, event):
00262         p = self.position_ids
00263         for i in ['from_id', 'to_id', 'length_id']:
00264             self.position_ids[i].configure(text = '')
00265 
00266     def get_length(self):
00267         self.sequence_length = len(self.sequence_id.get(1.0,END))
00268         return self.sequence_length
00269     
00270     def select_all(self, event):
00271         self.select(1, self.get_length())
00272         self.count_selection(None)
00273         
00274     def select(self, a, b):
00275         w = self.sequence_id
00276         w.selection_own()
00277         w.tag_add('sel', '1.%d' % (a - 1), '1.%d' % b)
00278         self.count_selection(None)
00279 
00280     def get_selection_or_sequence(self):
00281         w = self.sequence_id
00282         seq = self.get_selection()
00283         if not len(seq):
00284             seq = self.sequence_id.get(1.0,END)
00285 
00286         seq = re.sub('[^A-Z]','',seq)    
00287         return seq
00288     
00289     def get_selection(self):
00290         w = self.sequence_id
00291         #print w.selection_own()
00292         #w.selection_own()
00293         try:
00294             return w.selection_get()
00295             #return string.upper(w.get(sel.first, sel.last))
00296         except:
00297             return ''
00298         
00299     def get_self_selection(self):
00300         w = self.sequence_id
00301         #w.selection_own()
00302         try:
00303             return w.selection_get()
00304             #return string.upper(w.get(sel.first, sel.last))
00305             #return string.upper(w.selection_own_get())
00306         except:
00307             return ''
00308         
00309     def count_selection(self, event):
00310         w = self.sequence_id
00311         w.selection_own()
00312         try:
00313             a = int(w.index('sel.first').split('.')[1]) +1
00314             b = int(w.index('sel.last').split('.')[1])
00315             length = b - a + 1
00316 
00317             self.position_ids['from_id'].configure(text = 'Start:%d'% a)
00318             self.position_ids['to_id'].configure(text = 'Stop:%d'% b)
00319             self.position_ids['length_id'].configure(text = '%d nt' % length)
00320 
00321             self.statistics_ids['length_id'].configure(text = 'Length=%d' % length)
00322             seq = self.get_self_selection()
00323             for nt in ['A','C','G','T']:
00324                 n = seq.count(nt)
00325                 self.statistics_ids[nt].configure(text = '%s=%d' % (nt,n))
00326                 
00327             
00328         except:
00329             pass
00330         
00331     def position(self, event):
00332         x = event.x
00333         y = event.y
00334         pos = self.sequence_id.index('@%d,%d' % (x,y)).split('.')
00335         pos = int(pos[1]) + 1
00336         self.position_ids['id'].configure(text = str(pos))
00337         
00338     def open(self, file = None):
00339         if not file:
00340             file = askopenfilename()
00341         if not file: return
00342         genes = quick_FASTA_reader(file)
00343         self.insert_sequence(genes[0])
00344 
00345     def insert_sequence(self, (name, sequence)):
00346         self.sequence_id.delete(0.0, END)
00347         self.sequence_id.insert(END, sequence.upper())
00348         self.fix_sequence()
00349         self.update_label(name)
00350 
00351     def fix_sequence(self):
00352         seq = self.sequence_id.get(1.0,END)
00353         seq = seq.upper()
00354         seq = re.sub('[^A-Z]','',seq)
00355         self.sequence_id.delete(0.0,END)
00356         self.sequence_id.insert(END, seq)
00357         
00358     def update_label(self, header):
00359         name = header.split(' ')[0]
00360         name = name.split(',')[0]
00361         self.position_ids['label'].configure(text = name)
00362         
00363     def export(self):
00364         seq = self.get_self_selection()
00365         print seq, len(seq)
00366         
00367     def gcframe(self):
00368         seq = self.get_selection_or_sequence()
00369         if not seq: return
00370         np = NotePad()
00371         tid = np.text_id()
00372         tid.insert(END, self.translator.gcframe(seq, self.current_codon_table_id))
00373 
00374     def translate(self, frame = 1):
00375         seq = self.get_selection_or_sequence()
00376         if not seq: return
00377         np = NotePad()
00378         tid = np.text_id()
00379         tid.insert(END, self.translator.frame_nice(seq, frame, self.current_codon_table_id))
00380 
00381     def extract(self, frame = 1):
00382         seq = self.get_selection_or_sequence()
00383         if not seq: return
00384         aa_seq = self.translator.frame(seq, frame, self.current_codon_table_id)
00385         print '>%s<' % aa_seq
00386         aa_seq = re.sub('(.{50})','\\1\n',str(aa_seq))
00387         np = NotePad()
00388         tid = np.text_id()
00389         tid.insert(END,'>frame%d\n%s' % (frame,aa_seq))
00390 
00391     def statistics(self):
00392         seq = self.get_selection_or_sequence()
00393         if not seq: return
00394         seq = seq.upper()
00395         aa = {'A':0,'C':0,'G':0,'T':0,'N':0}
00396         for nt in seq:
00397             if nt not in aa: nt = 'N'
00398             aa[nt] = aa[nt] + 1
00399 
00400         GC = (100.0*(aa['G'] + aa['C']))/len(seq)
00401         
00402         np = NotePad()
00403         tid = np.text_id()
00404 
00405         tid.insert(END,"""%s
00406 
00407 Length = %d
00408 A=%d C=%d G=%d T=%d other=%d
00409 GC=%f
00410 
00411 """ % (time.strftime('%y %b %d, %X\n', time.localtime(time.time())),
00412                len(seq), aa['A'], aa['C'], aa['G'], aa['T'], aa['N'], GC)
00413                    )
00414         
00415     def blast(self):
00416         seq = self.get_selection_or_sequence()
00417         self.blaster = BlastIt(seq, self.parent)
00418 
00419     def reverse(self):
00420         w = self.sequence_id
00421         w.selection_own()
00422         try:
00423             start, stop = w.tag_ranges(SEL)
00424         except:
00425             start, stop = 1.0, self.sequence_id.index(END)
00426 
00427         seq = w.get(start, stop)
00428         seq = map(None,re.sub('[^A-Z]','',seq))
00429         seq.reverse()
00430         seq = ''.join(seq)
00431 
00432         w.delete(start, stop)
00433         w.insert(start, seq)
00434         w.tag_remove(SEL, 1.0, start)
00435         w.tag_add(SEL, start, stop)
00436         w.tag_remove(SEL, stop, END)
00437 
00438     def complement(self):
00439         w = self.sequence_id
00440         w.selection_own()
00441         try:
00442             start, stop = w.tag_ranges(SEL)
00443         except:
00444             start, stop = 1.0, self.sequence_id.index(END)
00445 
00446         seq = w.get(start, stop)
00447         seq = re.sub('[^A-Z]','',seq)    
00448 
00449         #print 'seq >%s<' % seq
00450         complementary = self.translator.complement(seq)
00451         w.delete(start, stop)
00452         w.insert(start, complementary)
00453         w.tag_remove(SEL, 1.0, start)
00454         w.tag_add(SEL, start, stop)
00455         w.tag_remove(SEL, stop, END)
00456              
00457     def antiparallel(self):
00458         w = self.sequence_id
00459         w.selection_own()
00460         try:
00461             start, stop = w.tag_ranges(SEL)
00462         except:
00463             start, stop = 1.0, self.sequence_id.index(END)
00464 
00465         seq = w.get(start, stop)
00466         seq = re.sub('[^A-Z]','',seq)    
00467 
00468         antip = self.translator.antiparallel(seq)
00469         w.delete(start, stop)
00470         w.insert(start, antip)
00471         w.tag_remove(SEL, 1.0, start)
00472         w.tag_add(SEL, start, stop)
00473         w.tag_remove(SEL, stop, END)
00474 
00475     def search(self):
00476         seq = self.get_selection_or_sequence()
00477         searcher = XDNAsearch(seq, master = self.sequence_id, highlight = 1)
00478 
00479     def goto(self, *args):
00480         pos = self.goto_entry.get()
00481         try:
00482             pos = int(pos) -1
00483         except:
00484             try:
00485                 start, stop = pos.split(':')
00486                 start = int(start)-1
00487                 stop = int(stop)
00488                 self.mark(start, stop)
00489                 return
00490             except:
00491                 import traceback
00492                 traceback.print_exc()
00493 
00494                 self.goto_entry.delete(0,END)
00495                 return
00496             
00497         self.sequence_id.focus()
00498         self.sequence_id.mark_set('insert','1.%d' % pos)
00499 
00500     def mark(self, start, stop):
00501         self.sequence_id.focus()
00502         self.sequence_id.mark_set('insert','1.%d' % start)
00503         self.sequence_id.tag_add(SEL, '1.%d' % start, '1.%d' % stop)
00504         
00505 if __name__ == '__main__':
00506     xbbtools = xbb_widget()
00507     xbbtools.main_frame.option_add('*frame.background', 'dimgrey')
00508     xbbtools.open('test.fas')