source: orange-bioinformatics/_bioinformatics/widgets/OWKEGGPathwayBrowser.py @ 1748:3a6c3d41aaee

Revision 1748:3a6c3d41aaee, 33.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 12 months ago (diff)

Test for the presence of 'self.signalManager'.

A default global signalManager is no longer provided by OWBaseWidget.

RevLine 
[188]1"""
[1462]2<name>KEGG Pathways</name>
[1051]3<description>Browse KEGG pathways that include an input set of genes.</description>
[1052]4<priority>2030</priority>
[1726]5<icon>icons/KEGGPathways.svg</icon>
[188]6"""
[1632]7
[1733]8from __future__ import absolute_import, with_statement
[1712]9
[188]10import sys
[1733]11import gc
[1739]12import webbrowser
13import threading
14from functools import partial
[1632]15from collections import defaultdict
[1740]16from operator import add, itemgetter
[1739]17
18from PyQt4.QtGui import (
19    QTreeWidget, QTreeWidgetItem, QItemSelectionModel, QSplitter,
20    QAction, QMenu, QGraphicsView, QGraphicsScene, QFont,
21    QBrush, QColor, QPen, QTransform, QPainter, QPainterPath,
22    QGraphicsItem, QGraphicsPathItem, QGraphicsPixmapItem, QPixmap
23)
24
25from PyQt4.QtCore import (
26    Qt, QObject, QMetaObject, QTimer, Q_ARG, QRectF, SIGNAL
27)
[1142]28
[1712]29import Orange
[1739]30
[1643]31from Orange.orng import orngMisc, orngServerFiles
[1632]32from Orange.orng.orngDataCaching import data_hints
[1739]33
[1632]34from Orange.OrangeWidgets import OWGUI
35from Orange.OrangeWidgets.OWWidget import *
[188]36
[1632]37from .. import obiTaxonomy
[1716]38from .. import obiKEGG
[1632]39from .. import obiGeneSets
[1118]40
[1534]41USE_THREADING = True
42
43if USE_THREADING:
[1739]44
[1534]45    def threading_queued_invoke(qobj, func):
46        def safe_wrapper(*args, **kwargs):
[1739]47            QMetaObject.invokeMethod(qobj, "queuedInvoke",
48                                     Qt.QueuedConnection,
[1534]49                                     Q_ARG("PyQt_PyObject",
50                                           partial(func, *args, **kwargs)))
51        return safe_wrapper
[1739]52
53
[211]54def split_and_strip(string, sep=None):
55    return [s.strip() for s in string.split(sep)]
56
[1739]57
58def path_from_graphics(graphics):
59    """
60    Return a constructed `QPainterPath` for a KEGG pathway graphics
61    element.
62
63    """
64    path = QPainterPath()
65    x, y, w, h = [int(graphics.get(c, 0)) for c in
66                  ["x", "y", "width", "height"]]
67    type = graphics.get("type", "rectangle")
68    if type == "rectangle":
69        path.addRect(QRectF(x - w / 2, y - h / 2, w, h))
70    elif type == "roundrectangle":
71        path.addRoundedRect(QRectF(x - w / 2, y - h / 2, w, h), 10, 10)
72    elif type == "circle":
73        path.addEllipse(QRectF(x - w / 2, y - h / 2, w, h))
74    else:
75        ValueError("Unknown graphcis type %r." % type)
76    return path
77
78
[1142]79class EntryGraphicsItem(QGraphicsPathItem):
[1739]80    """
81    An Graphics Item with actions for an overlay of a KEGG pathway image.
82    """
[1142]83    def __init__(self, graphics, *args):
84        QGraphicsPathItem.__init__(self, *args)
[1739]85        path = path_from_graphics(graphics)
[1142]86        self.setPath(path)
87        self.setAcceptHoverEvents(True)
88        self._actions = []
89        self.link = None
[1739]90
[1142]91    def hoverEnterEvent(self, event):
92        self.setBrush(QBrush(QColor(0, 100, 0, 100)))
[1739]93
[1142]94    def hoverLeaveEvent(self, event):
95        self.setBrush(QBrush(Qt.NoBrush))
[1739]96
[1142]97    def contextMenuEvent(self, event):
98        if self._actions:
99            self._menu = menu = QMenu()
100            for action in self._actions:
101                menu.addAction(action)
102            menu.popup(event.screenPos())
[1739]103
[1142]104    def itemChange(self, change, value):
105        if change == QGraphicsItem.ItemSelectedHasChanged:
106            self.setPen(QPen(Qt.red if self.isSelected() else Qt.blue, 2))
[1739]107
[1142]108        return QGraphicsPathItem.itemChange(self, change, value)
[1739]109
110
[1142]111class GraphicsPathwayItem(QGraphicsPixmapItem):
[1739]112    """
113    A Graphics Item displaying a KEGG Pathway image with optional
114    marked objects.
115
116    """
[1142]117    def __init__(self, pathway, objects, *args, **kwargs):
118        QGraphicsPixmapItem.__init__(self, *args)
119        self.setTransformationMode(Qt.SmoothTransformation)
120        self.setPathway(pathway)
[1739]121        self.setMarkedObjects(objects,
122                              name_mapper=kwargs.get("name_mapper", {}))
123
[1142]124    def setPathway(self, pathway):
[1739]125        """
126        Set pathway
127        """
[1142]128        self.pathway = pathway
129        if pathway:
130            image_filename = pathway.get_image()
131            self._pixmap = QPixmap(image_filename)
132        else:
133            self._pixmap = QPixmap()
134        self.setPixmap(self._pixmap)
[1739]135
[1142]136    def setMarkedObjects(self, objects, name_mapper={}):
[1577]137        for entry in self.pathway.entries() if self.pathway else []:
[1142]138            if entry.type == "group":
139                continue
140            graphics = entry.graphics
141            contained_objects = [obj for obj in objects if obj in entry.name]
142            item = EntryGraphicsItem(graphics, self, self.scene())
[1739]143            item.setToolTip(self.tooltip(entry, contained_objects,
144                                         name_mapper))
[1142]145            item._actions = self.actions(entry, contained_objects)
146            item.marked_objects = contained_objects
147            if contained_objects:
148                item.setPen(QPen(Qt.blue, 2))
149                item.setFlag(QGraphicsItem.ItemIsSelectable, True)
[1739]150
[1142]151    def actions(self, entry, marked_objects=[]):
152        actions = []
153        type = entry.type
154        if marked_objects:
155            action = QAction("View genes on kegg website", None)
156            org = set([s.split(":")[0] for s in marked_objects]).pop()
157            genes = [s.split(":")[-1] for s in marked_objects]
[1739]158            address = ("http://www.genome.jp/dbget-bin/www_bget?" +
159                       "+".join([org] + genes))
160            action.connect(action,
161                           SIGNAL("triggered()"),
162                           lambda toggled=False, address=address:
163                               webbrowser.open(address))
[1142]164            actions.append(action)
165        elif hasattr(entry, "link"):
166            action = QAction("View %s on KEGG website" % str(type), None)
[1739]167            action.connect(action,
168                           SIGNAL("triggered()"),
169                           lambda toggled=False, address=entry.link: \
170                               webbrowser.open(address))
[1142]171            actions.append(action)
172        return actions
[1739]173
[1142]174    def tooltip(self, entry, objects, name_mapper={}):
175        names = [obj for obj in objects if obj in entry.name]
176        names = [name_mapper.get(name, name) for name in names]
[1739]177        text = entry.name[:16] + " ..." if len(entry.name) > 20 else entry.name
178        text = "<p>%s</p>" % text
[1142]179        if names:
180            text += "<br>".join(names)
181        return text
[1739]182
[1142]183    def contextMenuEvent(self, event):
184        self._menu = menu = QMenu()
185        action = menu.addAction("View this pathway on KEGG website")
[1739]186        address = ("http://www.kegg.jp/kegg-bin/show_pathway?%s%s" %
187                   (self.pathway.org, self.pathway.number))
188        action.connect(action, SIGNAL("triggered()"),
189                       lambda: webbrowser.open(address))
[1142]190        menu.popup(event.screenPos())
[188]191
[1739]192
[195]193class PathwayView(QGraphicsView):
[188]194    def __init__(self, master, *args):
[195]195        QGraphicsView.__init__(self, *args)
[188]196        self.master = master
[1739]197
[195]198        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
199        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
[1739]200
[1142]201        self.setRenderHints(QPainter.Antialiasing)
202        scene = QGraphicsScene(self)
[1739]203        self.pixmapGraphicsItem = QGraphicsPixmapItem(None, scene)
[1142]204        self.setScene(scene)
[1739]205
[188]206        self.setMouseTracking(True)
207        self.viewport().setMouseTracking(True)
[1739]208
[1248]209        self.setFocusPolicy(Qt.WheelFocus)
[1739]210
[188]211    def SetPathway(self, pathway=None, objects=[]):
[1142]212        self.scene().clear()
[188]213        self.pathway = pathway
214        self.objects = objects
[1739]215        self.pathwayItem = GraphicsPathwayItem(
216            pathway, objects, None,
217            name_mapper=getattr(self.master, "uniqueGenesDict", {})
218        )
219
220        self.scene().addItem(self.pathwayItem)
[1142]221        self.scene().setSceneRect(self.pathwayItem.boundingRect())
222        self.updateTransform()
[1739]223
[1142]224    def resizeEvent(self, event):
225        self.updateTransform()
226        return QGraphicsView.resizeEvent(self, event)
[1739]227
[1142]228    def updateTransform(self):
229        if self.master.autoResize:
[1739]230            self.fitInView(self.scene().sceneRect().adjusted(-1, -1, 1, 1),
231                           Qt.KeepAspectRatio)
[188]232        else:
[1142]233            self.setTransform(QTransform())
[1739]234
[1248]235    def paintEvent(self, event):
236        QGraphicsView.paintEvent(self, event)
237        if getattr(self, "_userMessage", None):
238            painter = QPainter(self.viewport())
239            font = QFont(self.font())
240            font.setPointSize(15)
241            painter.setFont(font)
[1739]242            painter.drawText(self.viewport().geometry(), Qt.AlignCenter,
243                             self._userMessage)
[1248]244            painter.end()
[1739]245
246
[188]247class OWKEGGPathwayBrowser(OWWidget):
[1739]248    settingsList = ["organismIndex", "geneAttrIndex", "autoCommit",
249                    "autoResize", "useReference", "useAttrNames",
250                    "caseSensitive", "showOrthology"]
251
252    contextHandlers = {
253        "": DomainContextHandler(
254            "",
255            [ContextField("organismIndex",
256                          DomainContextHandler.Required +
257                          DomainContextHandler.IncludeMetaAttributes),
258             ContextField("geneAttrIndex",
259                          DomainContextHandler.Required +
260                          DomainContextHandler.IncludeMetaAttributes),
261             ContextField("useAttrNames",
262                          DomainContextHandler.Required +
263                          DomainContextHandler.IncludeMetaAttributes)]
264        )
265    }
266
[1462]267    def __init__(self, parent=None, signalManager=None, name="KEGG Pathways"):
[1525]268        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
[1739]269        self.inputs = [("Examples", Orange.data.Table, self.SetData),
270                       ("Reference", Orange.data.Table, self.SetRefData)]
271        self.outputs = [("Selected Examples", Orange.data.Table),
272                        ("Unselected Examples", Orange.data.Table)]
[188]273        self.organismIndex = 0
274        self.geneAttrIndex = 0
275        self.autoCommit = False
276        self.autoResize = True
277        self.useReference = False
[214]278        self.useAttrNames = 0
[188]279        self.caseSensitive = True
[519]280        self.showOrthology = True
[188]281        self.autoFindBestOrg = False
282        self.loadSettings()
283
284        self.controlArea.setMaximumWidth(250)
[1248]285        box = OWGUI.widgetBox(self.controlArea, "Info")
286        self.infoLabel = OWGUI.widgetLabel(box, "No data on input\n")
[1739]287
288        self.allOrganismCodes = {}
289
[1003]290        self.organismCodes = []
[1739]291
292        # Organism selection.
[1534]293        box = OWGUI.widgetBox(self.controlArea, "Organism")
[1739]294        self.organismComboBox = OWGUI.comboBox(
295            box, self, "organismIndex",
296            items=[],
297            callback=self.OrganismSelectionCallback,
298            addSpace=True,
299            debuggingEnabled=0,
300            tooltip="Select the organism of the input genes")
301
[1748]302        if self.signalManager:
303            self.signalManager.freeze(self).push()
[1739]304
305        # Selection of genes attribute
[1003]306        box = OWGUI.widgetBox(self.controlArea, "Gene attribute")
[1534]307        self.geneAttrCombo = OWGUI.comboBox(box, self, "geneAttrIndex",
308                                            callback=self.Update)
[1739]309
[1534]310        OWGUI.checkBox(box, self, "useAttrNames",
[1739]311                       "Use variable names",
[1534]312                       disables=[(-1, self.geneAttrCombo)],
313                       callback=self.UseAttrNamesCallback)
[1739]314
[216]315        self.geneAttrCombo.setDisabled(bool(self.useAttrNames))
[1739]316
[188]317        OWGUI.separator(self.controlArea)
[1739]318
[1534]319        OWGUI.checkBox(self.controlArea, self, "useReference",
320                       "From signal",
321                       box="Reference",
322                       callback=self.Update)
[1739]323
[188]324        OWGUI.separator(self.controlArea)
325
[1534]326        OWGUI.checkBox(self.controlArea, self, "showOrthology",
327                       "Show pathways in full orthology",
328                       box="Orthology",
329                       callback=self.UpdateListView)
[1739]330
[1534]331        OWGUI.checkBox(self.controlArea, self, "autoResize",
332                       "Resize to fit",
333                       box="Image",
334                       callback=self.UpdatePathwayViewTransform)
[1739]335
[1606]336        box = OWGUI.widgetBox(self.controlArea, "Cache Control")
[1739]337
338        OWGUI.button(box, self, "Clear cache",
[1606]339                     callback=self.ClearCache,
340                     tooltip="Clear all locally cached KEGG data.")
[1739]341
[188]342        OWGUI.separator(self.controlArea)
343
344        box = OWGUI.widgetBox(self.controlArea, "Selection")
345        OWGUI.checkBox(box, self, "autoCommit", "Commit on update")
[1378]346        OWGUI.button(box, self, "Commit", callback=self.Commit, default=True)
[188]347        OWGUI.rubber(self.controlArea)
[1739]348
[188]349        spliter = QSplitter(Qt.Vertical, self.mainArea)
350        self.pathwayView = PathwayView(self, spliter)
[195]351        self.mainArea.layout().addWidget(spliter)
[188]352
[195]353        self.listView = QTreeWidget(spliter)
354        spliter.addWidget(self.listView)
[1739]355
[195]356        self.listView.setAllColumnsShowFocus(1)
357        self.listView.setColumnCount(4)
[1739]358        self.listView.setHeaderLabels(["Pathway", "P value",
[1534]359                                       "Genes", "Reference"])
[195]360
[1739]361        self.listView.setSelectionMode(QTreeWidget.SingleSelection)
362
[195]363        self.listView.setSortingEnabled(True)
[1739]364
[188]365        self.listView.setMaximumHeight(200)
[1739]366
[1534]367        self.connect(self.listView,
368                     SIGNAL("itemSelectionChanged()"),
369                     self.UpdatePathwayView)
[1739]370
[1534]371        self.connect(self.graphButton,
372                     SIGNAL("clicked()"),
373                     self.saveGraph)
[1739]374
[195]375        self.ctrlPressed = False
[188]376        self.selectedObjects = defaultdict(list)
[212]377        self.data = None
[188]378        self.refData = None
[1739]379
[898]380        self.resize(800, 600)
[1739]381
[1534]382        self.connect(self,
383                     SIGNAL("widgetStateChanged(QString, int, QString)"),
384                     self.onStateChange)
[1739]385
[1534]386        self.has_new_data = False
387        self.has_new_reference_set = False
[1739]388
[1534]389        self.setEnabled(False)
390        QTimer.singleShot(100, self.UpdateOrganismComboBox)
[1733]391
[1003]392    def UpdateOrganismComboBox(self):
[1739]393        # First try to import slumber
[1582]394        try:
[1733]395            import slumber
[1582]396        except ImportError:
397            QMessageBox.warning(self,
[1733]398                "'slumber' library required.",
399                '<p>Please install '
400                '<a href="http://pypi.python.org/pypi/slumber">slumber</a> '
401                'library to use KEGG Pathways widget.</p>'
402            )
403
[1003]404        try:
[1534]405            genome = obiKEGG.KEGGGenome()
[1733]406
407            self.allOrganismCodes = genome
408
[1003]409            essential = genome.essential_organisms()
[1534]410            common = genome.common_organisms()
411            common = [c for c in common if c not in essential]
412
[1733]413            # TODO: Add option to specify additional organisms not
[1534]414            # in the common list.
415
[1733]416            self.infoLabel.setText("Fetching organism definitions\n")
417
418            keys = map(genome.org_code_to_entry_key, essential + common)
419
420            self.progressBarInit()
421            genome.pre_cache(keys, progress_callback=self.progressBarSet)
422            self.progressBarFinished()
423
424            codes = []
425            for code, key in zip(essential + common, keys):
426                codes.append((code, genome[key].definition))
427
428            items = [desc for code, desc in codes]
429
430            self.organismCodes = [code for code, desc in codes]
[1003]431            self.organismComboBox.addItems(items)
432        finally:
[1534]433            self.setEnabled(True)
434            self.infoLabel.setText("No data on input\n")
[1748]435            if self.signalManager:
436                self.signalManager.freeze(self).pop()
[1003]437
[1534]438    def Clear(self):
[1733]439        """
440        Clear the widget state.
441        """
[1534]442        self.infoLabel.setText("No data on input\n")
443        self.listView.clear()
444        self.ClearPathway()
[1733]445
[1534]446        self.send("Selected Examples", None)
447        self.send("Unselected Examples", None)
[1733]448
[1534]449    def ClearPathway(self):
450        self.pathwayView.SetPathway(None)
451        self.selectedObjects = defaultdict(list)
[1739]452
[188]453    def SetData(self, data=None):
454        self.closeContext()
455        self.data = data
[1248]456        self.warning(0)
[1534]457        if data is not None:
[1142]458            self.SetGeneAttrCombo()
[1118]459            taxid = data_hints.get_hint(data, "taxid", None)
[1142]460            if taxid:
461                try:
462                    code = obiKEGG.from_taxid(taxid)
463                    self.organismIndex = self.organismCodes.index(code)
464                except Exception, ex:
465                    print ex, taxid
[1739]466
467            self.useAttrNames = data_hints.get_hint(data, "genesinrows",
468                                                    self.useAttrNames)
469
[188]470            self.openContext("", data)
471        else:
[1534]472            self.Clear()
[1739]473
[1534]474        self.has_new_data = True
[188]475
476    def SetRefData(self, data=None):
477        self.refData = data
[1534]478        self.has_new_reference_set = True
[1739]479
[1534]480    def handleNewSignals(self):
481        if self.has_new_data or (self.has_new_reference_set and \
482                                 self.useReference):
[188]483            self.Update()
[1739]484
[1534]485            self.has_new_data = False
486            self.has_new_reference_set = False
[1739]487
[188]488    def UseAttrNamesCallback(self):
489        self.Update()
490
[1534]491    def OrganismSelectionCallback(self):
492        self.Update()
[1739]493
[1142]494    def SetGeneAttrCombo(self):
[1739]495        self.geneAttrCandidates = self.data.domain.variables + \
[1534]496                                  self.data.domain.getmetas().values()
[1739]497        self.geneAttrCandidates = filter(
498            lambda v: isinstance(v, (Orange.feature.Discrete,
499                                     Orange.feature.String)),
500            self.geneAttrCandidates)
501
[188]502        self.geneAttrCombo.clear()
[1739]503
504        self.geneAttrCombo.addItems([var.name for var in
505                                     self.geneAttrCandidates])
[1733]506
[1740]507        names_lower = [v.name.lower() for v in self.geneAttrCandidates]
508
509        scores = [(name == "gene", "gene" in name)
510                  for name in names_lower]
511        imax, _ = max(enumerate(scores), key=itemgetter(1))
512        self.geneAttrIndex = imax
513
[188]514    def UpdateListView(self):
[1248]515        self.bestPValueItem = None
[188]516        self.listView.clear()
[212]517        if not self.data:
518            return
[1733]519
[1003]520        allPathways = self.org.pathways()
521        allRefPathways = obiKEGG.pathways("map")
[1733]522
[188]523        items = []
[1606]524        self.progressBarInit()
[1534]525        kegg_pathways = obiKEGG.KEGGPathways()
[1606]526        kegg_pathways.pre_cache(self.pathways.keys(),
527                                progress_callback=self.progressBarSet)
528        self.progressBarFinished()
[1733]529
530        org_code = self.organismCodes[min(self.organismIndex,
[1739]531                                          len(self.organismCodes) - 1)]
[1733]532
[519]533        if self.showOrthology:
[1003]534            self.koOrthology = obiKEGG.KEGGBrite("ko00001")
[188]535            self.listView.setRootIsDecorated(True)
536            path_ids = set([s[-5:] for s in self.pathways.keys()])
[1733]537
[1000]538            def _walkCollect(koEntry):
539                num = koEntry.title[:5] if koEntry.title else None
[1733]540                if num in path_ids:
[1739]541                    return ([koEntry] +
542                            reduce(lambda li, c: li + _walkCollect(c),
543                                   [child for child in koEntry.entries],
544                                   []))
[188]545                else:
[1733]546                    c = reduce(lambda li, c: li + _walkCollect(c),
[1739]547                               [child for child in koEntry.entries],
548                               [])
[1000]549                    return c + (c and [koEntry] or [])
[1733]550
551            allClasses = reduce(lambda li1, li2: li1 + li2,
[1739]552                                [_walkCollect(c) for c in self.koOrthology],
553                                [])
[1733]554
[1000]555            def _walkCreate(koEntry, lvItem):
[195]556                item = QTreeWidgetItem(lvItem)
[1733]557                id = "path:" + org_code + koEntry.title[:5]
558
[1000]559                if koEntry.title[:5] in path_ids:
[1733]560                    p = kegg_pathways.get_entry(id)
[1534]561                    if p is None:
[1733]562                        # In case the genesets still have obsolete entries
[1534]563                        name = koEntry.title
564                    else:
565                        name = p.name
[188]566                    genes, p_value, ref = self.pathways[id]
[1534]567                    item.setText(0, name)
[188]568                    item.setText(1, "%.5f" % p_value)
[1733]569                    item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
570                    item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
[1534]571                    item.pathway_id = id if p is not None else None
[188]572                else:
[1733]573                    if id in allPathways:
574                        text = kegg_pathways.get_entry(id).name
575                    else:
576                        text = koEntry.title
577                    item.setText(0, text)
578
[188]579                    if id in allPathways:
580                        item.pathway_id = id
[1000]581                    elif "path:map" + koEntry.title[:5] in allRefPathways:
582                        item.pathway_id = "path:map" + koEntry.title[:5]
[188]583                    else:
584                        item.pathway_id = None
[1733]585
[1577]586                for child in koEntry.entries:
[188]587                    if child in allClasses:
[1000]588                        _walkCreate(child, item)
[1733]589
[1000]590            for koEntry in self.koOrthology:
591                if koEntry in allClasses:
592                    _walkCreate(koEntry, self.listView)
[1733]593
[195]594            self.listView.update()
[188]595        else:
596            self.listView.setRootIsDecorated(False)
597            pathways = self.pathways.items()
[1733]598            pathways.sort(lambda a, b: cmp(a[1][1], b[1][1]))
599
[188]600            for id, (genes, p_value, ref) in pathways:
[206]601                item = QTreeWidgetItem(self.listView)
[1534]602                item.setText(0, kegg_pathways.get_entry(id).name)
[188]603                item.setText(1, "%.5f" % p_value)
[1733]604                item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
605                item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
[188]606                item.pathway_id = id
607                items.append(item)
[1733]608
[188]609        self.bestPValueItem = items and items[0] or None
[195]610        self.listView.expandAll()
[898]611        for i in range(4):
612            self.listView.resizeColumnToContents(i)
[1739]613
[1248]614        if self.bestPValueItem:
[1739]615            index = self.listView.indexFromItem(self.bestPValueItem)
616            self.listView.selectionModel().select(
617                index, QItemSelectionModel.ClearAndSelect
618            )
[188]619
[195]620    def UpdatePathwayView(self):
621        items = self.listView.selectedItems()
[1739]622
[195]623        if len(items) > 0:
624            item = items[0]
[1248]625        else:
626            item = None
[1739]627
[1248]628        self.selectedObjects = defaultdict(list)
629        self.Commit()
630        item = item or self.bestPValueItem
631        if not item or not item.pathway_id:
632            self.pathwayView.SetPathway(None)
633            return
[1739]634
[1534]635        if USE_THREADING:
636            result = {}
[1739]637
[1534]638            def call(pathway_id):
639                result["pathway"] = p = obiKEGG.KEGGPathway(pathway_id)
[1739]640                p._get_kgml()  # makes sure the kgml file is downloaded
641                p._get_image_filename()  # makes sure the image is downloaded
642
[1534]643            self.setEnabled(False)
644            try:
[1739]645                thread = threading.Thread(None, call,
[1534]646                                          name="get_kgml_and_image",
647                                          args=(item.pathway_id,))
648                thread.start()
649                while thread.is_alive():
650                    thread.join(timeout=0.025)
651                    qApp.processEvents()
652            finally:
653                self.setEnabled(True)
654            if "pathway" in result:
655                self.pathway = result["pathway"]
656            else:
657                raise Exception("Could not get kgml and  pathway image")
658        else:
659            self.pathway = obiKEGG.KEGGPathway(item.pathway_id)
[1739]660
661        self.pathwayView.SetPathway(self.pathway,
662                                    self.pathways.get(item.pathway_id, [[]])[0])
663
[1534]664    def UpdatePathwayViewTransform(self):
665        self.pathwayView.updateTransform()
[1739]666
[188]667    def Update(self):
668        if not self.data:
669            return
670        self.error(0)
[211]671        self.information(0)
[1016]672        pb = OWGUI.ProgressBar(self, 100)
[188]673        if self.useAttrNames:
674            genes = [str(v.name).strip() for v in self.data.domain.attributes]
675        elif self.geneAttrCandidates:
[1739]676            geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
677                                                   len(self.geneAttrCandidates) - 1)]
678            genes = [str(e[geneAttr]) for e in self.data
679                     if not e[geneAttr].isSpecial()]
[211]680            if any("," in gene for gene in genes):
[1739]681                genes = reduce(add, (split_and_strip(gene, ",")
682                                     for gene in genes),
683                               [])
684                self.information(0,
685                                 "Separators detected in input gene names. "
686                                 "Assuming multiple genes per instance.")
[188]687        else:
[1534]688            self.error(0, "Cannot extract gene names from input")
[188]689            genes = []
[1739]690        org_code = self.organismCodes[min(self.organismIndex,
691                                          len(self.organismCodes) - 1)]
692
[1534]693        if USE_THREADING:
694            result = {}
[1739]695
[1534]696            def callable(*args, **kwargs):
697                result["org"] = org = obiKEGG.KEGGOrganism(org_code)
698                # Make sure genes are cached for global reference set
699                result["genes"] = org.genes.keys()
[1739]700
[1534]701            self.setEnabled(False)
702            try:
703                thread = threading.Thread(None, callable,
704                                          name="get_organism_genes",
705                                          )
706                thread.start()
707                while thread.is_alive():
708                    thread.join(timeout=0.025)
709                    qApp.processEvents()
710            finally:
711                self.setEnabled(True)
[1739]712
[1534]713            if "org" in result:
714                org = result["org"]
715            else:
716                raise Exception("Could not get organism genes")
717        else:
718            org = obiKEGG.KEGGOrganism(org_code)
[1739]719
720        uniqueGenes, _, _ = org.get_unique_gene_ids(set(genes),
721                                                    self.caseSensitive)
[1331]722        genesCount = len(set(genes))
[1739]723        self.infoLabel.setText("%i unique gene names on input\n%i (%.1f%%) "
724                               "genes names matched" %
725                               (genesCount, len(uniqueGenes),
726                                100.0 * len(uniqueGenes) / genesCount if genes else 0.0))
727
[211]728        self.information(1)
[188]729        if self.useReference and self.refData:
730            if self.useAttrNames:
731                reference = [str(v.name).strip() for v in self.refData]
732            else:
[1739]733                geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
734                                                       len(self.geneAttrCandidates) - 1)]
735                reference = [str(e[geneAttr]) for e in self.refData
736                             if not e[geneAttr].isSpecial()]
[211]737                if any("," in gene for gene in reference):
[1739]738                    reference = reduce(add, (split_and_strip(gene, ",")
739                                             for gene in reference),
740                                       [])
741                    self.information(1,
742                                     "Separators detected in reference gene "
743                                     "names. Assuming multiple genes per "
744                                     "example.")
745            uniqueRefGenes, _, _ = org.get_unique_gene_ids(set(reference),
746                                                           self.caseSensitive)
[188]747            self.referenceGenes = reference = uniqueRefGenes.keys()
748        else:
[1534]749            self.referenceGenes = reference = org.get_genes()
[188]750        self.uniqueGenesDict = uniqueGenes
751        self.genes = uniqueGenes.keys()
[1739]752        self.revUniqueGenesDict = dict([(val, key) for key, val in
753                                        self.uniqueGenesDict.items()])
754
[1534]755        taxid = obiKEGG.to_taxid(org.org_code)
[1739]756        r_tax_map = dict((v, k) for k, v in
757                         obiKEGG.KEGGGenome.TAXID_MAP.items())
[1534]758        if taxid in r_tax_map:
759            taxid = r_tax_map[taxid]
[1739]760
[1534]761        with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
[1554]762            orngServerFiles.update(obiGeneSets.sfdomain, "index.pck")
[1739]763            kegg_gs_collections = \
764                list(obiGeneSets.collections((("KEGG", "pathways"), taxid)))
765
[1534]766        if USE_THREADING:
767            result = {}
[1739]768
[1534]769            def callable(*args, **kwargs):
770#                result["result"] = org.get_enriched_pathways(*args, **kwargs)
771                result["result"] = pathway_enrichment(*args, **kwargs)
[1739]772
[1534]773            self.setEnabled(False)
774            try:
[1739]775                thread = threading.Thread(
776                    None, callable,
777                    name="get_enriched_pathways",
778                    args=(kegg_gs_collections,
779                          self.genes,
780                          reference),
781                    kwargs={"callback":
782                            threading_queued_invoke(
783                                self,
784                                lambda value: self.progressBarSet(value))}
785                )
786
[1534]787                thread.start()
788                while thread.is_alive():
789                    thread.join(timeout=0.025)
790                    qApp.processEvents()
791            finally:
792                self.setEnabled(True)
[1739]793
[1534]794            if "result" in result:
795                self.pathways = result["result"]
796            else:
797                raise Exception('Could not get enriched pathways')
[1739]798
[1534]799        else:
[1739]800            self.pathways = org.get_enriched_pathways(
801                self.genes, reference,
802                callback=self.progressBarSet
803            )
804
[1534]805        self.org = org
[1248]806        if not self.pathways:
807            self.warning(0, "No enriched pathways found.")
808        else:
809            self.warning(0)
[1739]810
[188]811        self.UpdateListView()
[1016]812        pb.finish()
[188]813
814    def Commit(self):
[212]815        if self.data:
[1142]816            selectedItems = self.pathwayView.scene().selectedItems()
[1739]817            selectedGenes = reduce(set.union, [item.marked_objects
818                                               for item in selectedItems],
819                                   set())
820
[212]821            if self.useAttrNames:
[1739]822                selectedVars = [self.data.domain[self.uniqueGenesDict[gene]]
823                                for gene in selectedGenes]
824                newDomain = Orange.data.Domain(selectedVars, 0)
825                data = Orange.data.Table(newDomain, self.data)
[1099]826                self.send("Selected Examples", data)
[1142]827            elif self.geneAttrCandidates:
[1739]828                geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
829                                                       len(self.geneAttrCandidates) - 1)]
[212]830                selectedExamples = []
831                otherExamples = []
832                for ex in self.data:
[1739]833                    names = [self.revUniqueGenesDict.get(name, None)
834                             for name in split_and_strip(str(ex[geneAttr]), ",")]
[212]835                    if any(name and name in selectedGenes for name in names):
836                        selectedExamples.append(ex)
837                    else:
838                        otherExamples.append(ex)
[1739]839
[1099]840                if selectedExamples:
[1739]841                    selectedExamples = Orange.data.Table(selectedExamples)
[1099]842                else:
843                    selectedExamples = None
[1739]844
[1099]845                if otherExamples:
[1739]846                    otherExamples = Orange.data.Table(otherExamples)
[1099]847                else:
848                    otherExamples = None
[1739]849
[1099]850                self.send("Selected Examples", selectedExamples)
851                self.send("Unselected Examples", otherExamples)
[188]852        else:
[212]853            self.send("Selected Examples", None)
854            self.send("Unselected Examples", None)
[1739]855
[1606]856    def ClearCache(self):
[1716]857        from ..obiKEGG import caching
[1606]858        try:
859            caching.clear_cache()
860        except Exception, ex:
[1739]861            QMessageBox.warning(self, "Cache clear", ex.args[0])
[188]862
[1248]863    def onStateChange(self, stateType, id, text):
864        if stateType == "Warning":
865            self.pathwayView._userMessage = text
866            self.pathwayView.viewport().update()
[1739]867
[1525]868    def saveGraph(self):
[1632]869        from Orange.OrangeWidgets.OWDlgs import OWChooseImageSizeDlg
[1525]870        sizeDlg = OWChooseImageSizeDlg(self.pathwayView.scene(), parent=self)
871        sizeDlg.exec_()
[188]872
[1534]873    @pyqtSignature("queuedInvoke(PyQt_PyObject)")
874    def queuedInvoke(self, func):
875        func()
[1733]876
[1534]877    def progressBarSet(self, value):
878        if not getattr(self, "_in_progress_update", False):
879            self._in_progress_update = True
880            try:
881                OWWidget.progressBarSet(self, value)
882            finally:
883                self._in_progress_update = False
[1733]884
[1534]885    def onDeleteWidget(self):
[1739]886        """
887        Called before the widget is removed from the canvas.
[1534]888        """
889        self.org = None
[1733]890        gc.collect()  # Force collection
[1739]891
892
[1632]893from .. import obiProb
[1534]894
[1739]895
[1534]896def pathway_enrichment(genesets, genes, reference, prob=None, callback=None):
897    result_sets = []
898    p_values = []
899    if prob is None:
900        prob = obiProb.Hypergeometric()
[1739]901
[1534]902    for i, gs in enumerate(genesets):
903        cluster = gs.genes.intersection(genes)
904        ref = gs.genes.intersection(reference)
905        k = len(cluster)
906        N = len(reference)
907        m = len(ref)
908        n = len(genes)
909        if k:
910            p_val = prob.p_value(k, N, m, n)
911            result_sets.append((gs.id, cluster, ref))
912            p_values.append(p_val)
913        if callback is not None:
[1554]914            callback(100.0 * i / len(genesets))
[1739]915
[1534]916    # FDR correction
917    p_values = obiProb.FDR(p_values)
[1739]918
919    return dict([(id, (genes, p_val, len(ref)))
920                 for (id, genes, ref), p_val in zip(result_sets, p_values)])
921
[1733]922
923if __name__ == "__main__":
[188]924    app = QApplication(sys.argv)
[1739]925    data = Orange.data.Table("brown-selected.tab")
[188]926    w = OWKEGGPathwayBrowser()
[1142]927    w.UpdateOrganismComboBox()
[188]928    w.show()
[1739]929    w.SetData(Orange.data.Table(data[:]))
[1534]930    QTimer.singleShot(10, w.handleNewSignals)
[1733]931
[206]932    app.exec_()
[188]933    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.