• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

#!/usr/bin/python
#  Copyright (C) 2006-2007 Free Software Foundation
 
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
 
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
#############################################################
#            I M P O R T I N G   M O D U L E S
#############################################################
 
import os
import sys
import math
import tempfile
import utils
 
from qt import *
from utils import *
 
from shutil import rmtree
from ViewerGui import ViewerGui
from ViewerWidget import NEW_REGION_SIG, NO_REGION_SIG, MAPPING_CHANGED_SIG
from ScoreGui import ScoreGui
from SpeciesGenomesDialog import SpeciesGenomesDialog
from ProbesListDialog import SELECT_SIG, PROBE_START_POS, PROBE_STOP_POS, ProbesListDialog
from UserManuelGui import UserManuelGui
from pselfiles import load_mapping, save_mapping, FILE_FORMAT_EXT
import pselfiles as PSF
from pkg_resources import resource_filename
import pipviewer
 
 
#############################################################
#              G L O B A L   C O N S T A N T S
#############################################################
 
# the number of "pages" on the scrollbar resolution
PAGES = 200
 
 
#############################################################
#                  M A I N   C L A S S
#############################################################
 
class ViewerWin(ViewerGui):
    """ This class define a ViewerWin for visualise and setting differents
    parameters on a multiple alignment. These parameter includes the 
    setting of region type, the file manipulation, the views options and 
    the navigation inside the alignment.
    """
    def __init__(self,parent=None,name=None):
        ViewerGui.__init__(self,parent,name)
        self.connect(self.widthSpn, SIGNAL("valueChanged(int)"),
                    self.setWinSize)
        self.connect(self.scroll, SIGNAL("valueChanged(int)"),
                    self.viewer.setPos)
        self.connect(self.skipBtn, SIGNAL("clicked()"),
                    self.viewer.reset_region)
        self.connect(self.probeBtn, SIGNAL("clicked()"),
                    self.viewer.setRegionAsProbe)
        self.connect(self.breakPointBtn, SIGNAL("clicked()"),
                    self.viewer.setRegionAsBreakPoint)
        self.connect(self.trashBtn, SIGNAL("clicked()"),
                    self.viewer.setRegionAsTrash)
        self.connect(self.clearBtn, SIGNAL("clicked()"),
                    self.viewer.clear_region)
        self.connect(self.scoreBtn, SIGNAL("clicked()"),
                    self.check_score)
        self.connect(self.viewer, NEW_REGION_SIG, self.show_region)
        self.connect(self.viewer, NO_REGION_SIG, self.reset_region)
        self.connect(self.viewer, MAPPING_CHANGED_SIG, self.update_mapping_stats)
        self.connect(self.viewer, MAPPING_CHANGED_SIG, self.update_probes_list)
 
        self.regTypeBtnsOut = [self.scoreBtn, self.breakPointBtn,
        		self.probeBtn, self.trashBtn]	
        self.regTypeBtnsIn = [self.scoreBtn, self.breakPointBtn,
                        self.probeBtn, self.trashBtn, self.clearBtn]
        self.regTypeBtnsIntersect = [self.breakPointBtn, self.probeBtn, 
                        self.trashBtn]
 
        # Probes List Dialog
        self.probesList = ProbesListDialog()
        self.connect(self.probesList.lstViewProbesList, SELECT_SIG, self.select_probe)
 
        # User Manuel Dialog
        self.userManuel = UserManuelGui()        
 
        # If true, ask the user for saving change at closing
        self.dirty = False
 
 
 
    def setParam(self, mapping):
        """ Matator for setting the initial parameters when the application
        started. Take the hashMap containing the data of the alignment as
        parameter.
        """
        self.mapping = mapping
        self.viewer.update()
        self.viewer.setMapping(mapping)
        width = len(self.viewer.align[0])
        self.widthSpn.setMaxValue(width)
        self.widthSpn.setValue((width / PAGES) or width)
        self.numProbeLbl.setNum(len(mapping[PSF.PROBES]))
        self.setViewAnnotationsStatus()
 
 
    def setViewAnnotationsStatus(self):
        """ Mutator for setting the initial state of the 'View Genes
        Annotations' toggle menu item.
        """
        if self.mapping[PSF.GENES_ANNOTATIONS]:
            self.viewsShowGenesAnnotationsAction.setEnabled(True)
            self.viewsShowGenesAnnotationsAction.setOn(True)
            self.viewer.setAnnotationsView(True)
        else:
            self.viewsShowGenesAnnotationsAction.setOn(False)
            self.viewsShowGenesAnnotationsAction.setEnabled(False)
            self.viewer.setAnnotationsView(False)
 
 
    def setMapping(self, filename):
        """ Mutator for setting the mapping containing the data of the 
        multiple alignement. Take the filename as parameter.
        """
        self.file = filename
        mapping = load_mapping(filename)
        self.setParam(mapping)
 
 
    def setWinSize(self, val):
        """ Mutator for setting the size of the viewer window and
        scroll bar. Take the position of the scroll bar as parameter.
        """
        self.viewer.setWinSize(val)
        width = len(self.viewer.align[0])
        self.scroll.setMaxValue(width-val)
        self.scroll.setPageStep(val)
 
 
    def setButtonsState(self, beg, end):
        """ Mutator for setting the states of the different buttons present
        in the main window. The buttons are 'check score', 'skip', 'clear',
        'probe', 'break-point' and 'trash'. Take the begining and ending pos
        of the selection window as parameters.
        """
        # Untill proven guilty we are not in a hightlighted region
        for btn in self.regTypeBtnsOut:
            btn.setEnabled(True)
 
        # Parse the highlighted region in the mapping
        for k, typeBtn in [(PSF.PROBES, self.probeBtn), 
                        (PSF.BREAKS, self.breakPointBtn), 
                        (PSF.TRASHS, self.trashBtn)]:          
            for reg in self.mapping[k]:
                start, stop = reg
 
                # Button state when a hightlighted region is selected
                if beg == start and end == stop:
                    for btn in self.regTypeBtnsIn:
                        btn.setEnabled(True)
                    typeBtn.setEnabled(False)
 
                # Button state when the selection intersect with a hightlighted region
                if (beg < start <= end) or (beg <= stop < end) or (beg > start and end < stop):
                    for btn in self.regTypeBtnsIntersect:
                        btn.setEnabled(False)	
                    self.clearBtn.setEnabled(True)
 
 
    def closeEvent(self, event):
        """ Action performed when the user close the main application.
        """
        # Signal the user for saving modification before closing
        if self.dirty:
            msg = "The file %s has been modified.\n\nDo you want to save it?"\
                    %(repr(os.path.split(self.file)[1]))
            returnVal = QMessageBox.warning(self, "Close the program - pipviewer",
                        msg, QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if returnVal == QMessageBox.Yes:
                self.fileSave()
                event.accept()
            elif returnVal == QMessageBox.No:
                event.accept()
            elif returnVal == QMessageBox.Cancel:
                event.ignore()
        else:
            event.accept()
 
 
    def fileExit(self):
        self.close()
 
 
    def fileSave(self):
        """ Action performed when the user select the 'file-->save' 
        menu item.
        """
        save_mapping(self.mapping, open(self.file, "w"))
        self.dirty = False
        self.statusBar().message("File has been saved successfully");
 
 
    def fileOpen(self):
        """ Action performed when the user select the 'file-->open' 
        menu item.
        """
        returnVal = None
 
        # Signal the user for saving modification before opening a new file
        if self.dirty:
            msg = "The file %s has been modified.\n\nDo you want to save it before opening a new file?"\
                    %(repr(os.path.split(self.file)[1]))
            returnVal = QMessageBox.warning(self, "Open File - pipviewer", 
                        msg, QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if returnVal == QMessageBox.Yes:
                self.fileSave()
 
        # Open a file Dialog for opening file
        if returnVal != QMessageBox.Cancel: 
            fileName = QFileDialog.getOpenFileName(
                    os.path.split(self.file)[0],
                    "Probes (*.psel);; All(*.*)",
                    self,
                    "open file dialog",
                    "Choose a file to open" );
 
            # Open the file if it exist
            if fileName:
                try:
                    self.setMapping(unicode(fileName))
                    self.statusBar().message( "File has been opened successfully")
                except PSF.FileFormatError:
                    QMessageBox.warning(self, "Warning - pipviewer", 
                            "The file is not a valid pipviewer file.")
 
 
    def fileSaveAs(self):
        """ Action performed when the user select the 'file-->save as' 
        menu item.
        """
        # Open a file dialog for saving file
        fileName = QFileDialog.getSaveFileName(
                self.file,
                "Probes (*.psel)",
                self,
                "save file dialog;; All (*.*)",
                "Choose a filename to save under")
 
        # Signal the user if the file already exist
        if fileName:
            self.file = str(fileName.ascii())
            if os.path.isfile(unicode(fileName)):
                msg = "The file %s already exist. Do you want to overwrite it?"\
                        %(repr(os.path.split(self.file)[1]))
                returnVal = QMessageBox.warning(self, "Overwrite the file? - pipviewer", 
                            msg, QMessageBox.Yes, QMessageBox.No)
 
                # Save the file to local disk
                if returnVal == QMessageBox.Yes:
                    save_mapping(self.mapping, open(unicode(fileName), "w"))
            else:
                save_mapping(self.mapping, open(unicode(fileName), "w"))
 
            self.dirty = False
            self.statusBar().message( "File has been saved successfully")
 
 
    def fileMerge(self):
        """ Action performed when the user select the 'file-->merge' 
        menu item.
        """
        # FIXME: this should go in pselfiles.py
        # Open a file dialog for opening file
        fileName = QFileDialog.getOpenFileName(
                "",
                "Probes (*.psel);; All(*.*)",
                self,
                "open file dialog",
                "Choose a file to open" );
 
        # Open the file if it exist
        if fileName:
            newMap = load_mapping(open(unicode(fileName)))
 
            # Merge the new file with the one in use
            if newMap[PSF.ALIGN] == self.mapping[PSF.ALIGN]:
                oldMap = self.mapping
                for k in PSF.REGION_TYPES:
                    oldMap[k] = remove_dups(oldMap[k]+newMap[k])
                self.update_mapping_stats()
            else:
                msg = "Can't merge probes from different alignments"
                QMessageBox.critical(self, "Bad Align", msg, True)
 
 
    def fileExportProbes(self):
        """ Action performed when the user select the 'file-->export probes' 
        menu item.
        """
        #Open a file dialog for chosing directory
        dir = QFileDialog.getExistingDirectory("")
 
        # Chose a suffix if dir exist
        if dir:
            suffix, ok = QInputDialog.getText("Probe Set Suffix", "Enter a suffix (optional):")
            suffix = ok and str(suffix) or ""
            probes = self.mapping[PSF.PROBES]
            probes.sort()
 
            label_width = int(math.floor(math.log10(len(probes)))+1)
            ref = self.getProbeSeq(0, len(self.mapping[PSF.ALIGN][0]))
 
            # Export the probe to file in the selected directory
            for i, probe in enumerate(probes):
                data = self.getProbeSeq(*probe)
                pos = ref.find(data)
                name = self.mapping[PSF.PROBES_IDS][probe]
                filename = os.path.join(str(dir), name + ".fas")
                annot = name+" "+self.mapping[PSF.SPECIES][0]+" " + \
                        str(pos+1)+":"+str(pos+len(data))
                print filename
                body = "\n".join(slice_fas(data))
                open(filename, "w").write(">%s\n%s\n" % (annot, body))
 
 
    def import_file(self, mapping, fileName):
        """ Import different multiple alignement file and save their
        content to the mapping. File can be ClustalW or PipMaker. Take
        the filename and the hashMap containing the data of the
        alignment as parameters.
        """
        self.setParam(mapping)
        # FIXME: we should be able to use unicode here
        base = os.path.splitext(str(fileName.ascii()))
        self.file = base[0] + PSF.FILE_FORMAT_EXT
        self.dirty = True
 
 
    def fileImportPipmaker(self):
        """ Action performed when the user select the 'file -> Import
        -> Pipmaker Multiple Alignement' menu item.
        """
        returnVal = None
 
        # Signal the user for saving modification before importing
        if self.dirty:
            msg = ("The file %s has been modified.\n\n"
                   "Do you want to save it before importing a new file?")
            msq = msg % (repr(os.path.split(self.file)[1]))
            returnVal = QMessageBox.warning(self, "Import File - pipviewer", 
                                            msg,
                                            QMessageBox.Yes,
                                            QMessageBox.No,
                                            QMessageBox.Cancel)
            if returnVal == QMessageBox.Yes:
                self.fileSave()
 
        # Open a file dialog for opening file
        if returnVal != QMessageBox.Cancel: 
            fileName = QFileDialog.getOpenFileName(
                "",
                "PipMaker Alignment File (*.txt);; All(*.*)",
                self,
                "open file dialog",
                "Choose a file to import");
 
            # Import the Pipmaker file if it exist
            if fileName:
                try:
                    mapping = PSF.import_multipipmaker_file(unicode(fileName))
                    self.import_file(mapping, fileName)
                    self.statusBar().message( "File has been imported successfully")
                except:
                    QMessageBox.warning(self, "Warning - pipviewer", 
                        "The file is not a valid Multipipmaker multiple alignment.")
 
 
    def fileImportClustal(self):
        """ Action performed when the user select the 'file-->Import-->ClustalW 
        Multiple Alignement' menu item.
        """
        returnVal = None
 
        # Signal the user for saving modification before importing
        if self.dirty:
            msg = "The file %s has been modified.\n\nDo you want to save it before importing a new file?"\
                    %(repr(os.path.split(self.file)[1]))
            returnVal = QMessageBox.warning(self, "Import File - pipviewer", 
                        msg, QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if returnVal == QMessageBox.Yes:
                self.fileSave()
 
        # Open a file dialog for opening file
        if returnVal != QMessageBox.Cancel: 
            fileName = QFileDialog.getOpenFileName(
                "",
                "ClustalW Alignment File (*.aln);; All(*.*)",
                self,
                "open file dialog",
                "Choose a file to import");
 
            # Import the ClustalW file if it exist
            if fileName:
                try:
                    mapping = PSF.import_clustalw_file(unicode(fileName))
                    self.import_file(mapping, fileName)
                    self.statusBar().message( "File has been imported successfully")
                except:
                    QMessageBox.warning(self, "Warning - pipviewer", 
                        "The file is not a valid ClustalW multiple alignment.")
 
 
    def viewShowAnnotations(self):
        """ Action performed when the user select the toggle menu item 
        'Views-->View Genes Annotations'. This item is only active if the 
        genes annotations have been compute or saved in the .psel file
        """
        if self.viewsShowGenesAnnotationsAction.isOn():
            self.viewer.setAnnotationsView(True)
            self.viewer.update()
        else:
            self.viewer.setAnnotationsView(False)
            self.viewer.update()
 
 
    def viewProbesList(self):
        """ Action performed when the user select the menu item 'Views-->
        Probes List'.
        """
        self.probesList.show()
        self.probesList.raiseW()
        self.probesList.setActiveWindow()
 
 
    def toolAssociateGenome(self):
        """ Action performed when the user select the menu item 'Tools-->
        Associate Genomes'.
        """
        dia = SpeciesGenomesDialog(self)
        speciesNumber = 0
 
        # Fill the genomes listView with the acession number in the mapping
        for i, species in enumerate(self.mapping[PSF.SPECIES]):
            speciesNumber = speciesNumber + 1
            if i == 0:
                QListViewItem(dia.genomesLst, str(speciesNumber), species+"(ref)",
                            self.mapping[PSF.REFSEQ_IDS].get(i, ""))
            else:
                QListViewItem(dia.genomesLst, str(speciesNumber), species, 
                            self.mapping[PSF.REFSEQ_IDS].get(i, ""))
 
        okVal = dia.exec_loop()
 
        # Add the accession number from the listView to the mapping
        if dia.Accepted == okVal: 
            item = dia.genomesLst.firstChild()
            while item:
                noSpecies = int(str(item.text(0)))-1
                self.mapping[PSF.REFSEQ_IDS][noSpecies] = unicode(item.text(2))
                item = item.nextSibling()
            self.dirty = True
 
 
    def toolComputeAnnotations(self):
        """ Action performed when the user select the menu item 'Tools-->
        Compute Genes Annotations'.
        """
        alignRefGenome = self.mapping[PSF.ALIGN][0]
        genomeLengthWithoutIR = self.viewer.posToRefPos(len(alignRefGenome))
        oldCursor = QCursor(self.cursor())
        waitCursor = QCursor(Qt.WaitCursor)
 
        # Compute the genes annotations from a genbank file if not present in
        # the mapping
        if not self.mapping[PSF.GENES_ANNOTATIONS]:
            self.setCursor(waitCursor)
            self.alignTxt.setCursor(waitCursor)
            try: 
                refGenomeFile = utils.genome_file(self.mapping, 0, "gb", True)
                annotations = utils.ref_genome_annotations(refGenomeFile, genomeLengthWithoutIR)
                alignAnnotations = utils.adjust_annotations_on_align(annotations, alignRefGenome)
                self.mapping[PSF.GENES_ANNOTATIONS] = alignAnnotations
                self.viewer.update()
                self.statusBar().message("Computation terminated.")
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
                self.dirty = True
                self.setViewAnnotationsStatus()
            except Exception:
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
                msg = "Cannot compute the ref. genome without his accession number.\
                        \n\nUse the 'Associate Genomes..' function to chose the accession number of the ref. genome."
                QMessageBox.warning(self, "Warning - Pipviewer", msg)
        else:
            msg = "Compute of genes annotation has already been performed or saved."
            QMessageBox.information(self, "Information - Pipviewer", msg)
 
 
    def helpUserManuel(self):
        """ Action performed when the user select the menu item 'help-->
        User Manuel'.
        """
        self.userManuel.txtBrwUserMan.setTextFormat(Qt.RichText)
        path = resource_filename(pipviewer.__name__, "guide.html")
        txt = unicode(open(path).read(), "utf-8")
        self.userManuel.txtBrwUserMan.setText(txt)
        self.userManuel.show()
        self.userManuel.raiseW()
        self.userManuel.setActiveWindow()
 
 
    def helpAbout(self):
        """ Action performed when the user select the menu item 'Help->about'.
        """
        msg = ("pipviewer %s \n"
               "Visualize multiple alignments and annotate regions.\n"
               ) % pipviewer.__version__
        QMessageBox.information(self, "About pipviewer", msg, True)
 
 
    def update_mapping_stats(self):
        """ Update the mapping data each time the user set a new probe.
        """
        self.numProbeLbl.setNum(len(self.mapping[PSF.PROBES]))
        self.dirty = True
 
 
    def update_probes_list(self):
        """ Update the probes list view each time the user set a new probe.
        """
        self.probesList.lstViewProbesList.clear()
        for i, probes in enumerate(self.mapping[PSF.PROBES]):
            probeName = self.mapping[PSF.PROBES_IDS][probes]
            start = probes[0]
            stop = probes[1]
            length = stop-start
            QListViewItem(self.probesList.lstViewProbesList, str(i+1), probeName, 
                        str(start+1), str(stop), str(length))
 
 
    def select_probe(self, item):
        """ Loacate and select the probe selected in the probes list. 
        Take the item selected from the probes list as parameter.
        """
        probeLocation = ()
 
        if item:
            start = int(str(item.text(PROBE_START_POS)))
            stop = int(str(item.text(PROBE_STOP_POS)))
            probeLocation = (start-1, stop)
 
            # Locate the probe in the conservation viewer
            adjustPos = (self.widthSpn.value()/2)-((stop-start)/2)
            self.scroll.setValue(start-adjustPos)
 
            # Select the probe
            self.viewer.selectStart = start-1
            self.viewer.selectStop = stop
            self.viewer.update()
 
            # Set the button state
            self.setButtonsState(start-1, stop)
 
 
    def getProbeSeq(self, x0, x1):
        """ Accessor that return a probe's nucleotide sequence. Take the starting
        and ending position as parameters.
        """
        return self.viewer.getProbeSeq(x0, x1)
 
 
    def check_score(self):
        """ Action performed when the user press the button 'Check Score'. This
        button is only active when their is a selected region in the viewer.
        """
        #TODO : Implement a dynamique global alignment algorithm
        oldCursor = QCursor(self.cursor())
        waitCursor = QCursor(Qt.WaitCursor)
 
        # Check the score of a selected region by using Blast2seq
        if self.viewer.getSelection():
            self.setCursor(waitCursor)
            self.alignTxt.setCursor(waitCursor)
            x0, x1 = self.viewer.getSelection()
            region = self.getProbeSeq(x0, x1)
            m_dir = tempfile.mkdtemp()
            res_dir = tempfile.mkdtemp()
 
            open(os.path.join(m_dir, "probe"), "w").write(
            ">probe\n%s\n" % region)
 
            cmd = "vhybridize -l 0 -i 0 -p %s -o %s " % (m_dir, res_dir)
            # FIXME: auto d/l should be configurable
            genomes = []
            try:
                # Download the species genomes
                genomes = filter(lambda s:s,
                        [genome_file(self.mapping, i, "fasta", True)
                        for i in range(len(self.mapping[PSF.SPECIES]))])
                if not genomes:
                    raise EOFError("Warning! No Acession Number")
 
                revMap = {}
                genomes = []
                for i, species in enumerate(self.mapping[PSF.SPECIES]):
                    genome = genome_file(self.mapping, i, "fasta", True)
                    if genome:
                        genomes.append(genome)
                        revMap[os.path.splitext(os.path.basename(genome))[0]] = species
 
                full_cmd = cmd + " ".join(genomes)
                print "full_cmd:", full_cmd
 
                # call vHybridize.py
                if os.system(full_cmd):
                    raise OSError("problems with vhybridize.py")
 
                dia = ScoreGui(self)
 
                # parse vhybridize output
                for f in filter(lambda f:os.path.splitext(f)[1] == ".vhy",
                                os.listdir(res_dir)):
                    #(start, stop, marker, %length, %identity)
                    lines = filter(lambda l:l and l[0] == "(",
                    open(os.path.join(res_dir, f)).readlines())
 
                    # FIXME: eval is not safe, we need proper parsing
                    lines = map(eval, lines)
                    lines = [(line[3], line[4], line) for line in lines]
                    lines.sort()
                    line = lines[-1][-1]
                    start, stop, name, length, identity = line[:5]
 
                    # FIXME: use the species name, not the filename
                    item = QListViewItem(dia.hitsLst,
                            revMap[os.path.splitext(f)[0]],
                            str(length),
                            str(identity))
 
                # cleanup
                rmtree(m_dir)
                rmtree(res_dir)
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
 
                dia.show()
                #print "region:", region
 
            except URLError:
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
                QMessageBox.warning(self, "Warning - pipviewer", 
                    "One or many accession number are invalid.")
            except EOFError:
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
                msg = ("There is no accession number associated with the"
                       " species genomes. \n"
                       "Use the 'tools -> associate genomes' option to"
                       " specify the accession numbers.")
                QMessageBox.warning(self, "Warning - pipviewer", msg)
            except OSError:
                self.setCursor(oldCursor)
                self.alignTxt.setCursor(oldCursor)
                msg = ("Problems with 'vhybidize'. \n"
                       "Is it installed properly?")
                QMessageBox.warning(self, "Warning - pipviewer", msg)
 
 
    def reset_region(self):
        """ Reset the informations showing when no region is selected in the
        viewer.
        """
        for btn in self.regTypeBtnsIn:
            btn.setEnabled(False)
 
        # Reset the text field that show the selected region
        self.alignTxt.clear()
 
        # Reset the reference sequence information
        self.startLbl.setText("-")
        self.stopLbl.setText("-")
        self.lengthLbl.setText("-")
 
        # Reset the alignment sequence information
        self.probeStartLbl.setText("-")
        self.probeStopLbl.setText("-")
        self.probeLengthLbl.setText("-")
 
 
    def show_region(self, txt, beg, end):
        """ Show the various informations when a region is selected in the
        viewer. Take the aligned sequences text and the begining and ending
        position of the selected region in the viewer as parameters.
        """
        # Set the aligned nucleotide sequences in the text field window
        self.alignTxt.clear()
        self.alignTxt.setText(txt)
 
        # Set the starting and ending position of the selection
        self.startLbl.setNum(beg+1)
        self.stopLbl.setNum(end)
        m_start = self.viewer.posToRefPos(beg)
        m_len = len(self.getProbeSeq(beg, end))
        self.probeStartLbl.setNum(m_start+1)
        self.probeStopLbl.setNum(m_start + m_len)
 
        # Set the selected region length
        self.lengthLbl.setNum(end-beg)
        self.probeLengthLbl.setNum(m_len)
 
        # Set the button states
        self.setButtonsState(beg, end)
 
 
def qt_run(filename):
    """ Set the application parameters and show the main window at startup.
    Take the filename as parameter.
    """
    app = QApplication(sys.argv)
    win = ViewerWin()
    win.setMapping(filename)
    app.setMainWidget(win)
    win.show()
    app.exec_loop()
 
 
def main():
    if len(sys.argv) != 2:
        print "USAGE: %s FILE" % sys.argv[0]
        print "  where FILE is a .psel file",
        sys.exit(1)
    else:
        qt_run(sys.argv[1])
 
if __name__ == "__main__":
    main()