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.

Line 
1"""
2<name>KEGG Pathways</name>
3<description>Browse KEGG pathways that include an input set of genes.</description>
4<priority>2030</priority>
5<icon>icons/KEGGPathways.svg</icon>
6"""
7
8from __future__ import absolute_import, with_statement
9
10import sys
11import gc
12import webbrowser
13import threading
14from functools import partial
15from collections import defaultdict
16from operator import add, itemgetter
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)
28
29import Orange
30
31from Orange.orng import orngMisc, orngServerFiles
32from Orange.orng.orngDataCaching import data_hints
33
34from Orange.OrangeWidgets import OWGUI
35from Orange.OrangeWidgets.OWWidget import *
36
37from .. import obiTaxonomy
38from .. import obiKEGG
39from .. import obiGeneSets
40
41USE_THREADING = True
42
43if USE_THREADING:
44
45    def threading_queued_invoke(qobj, func):
46        def safe_wrapper(*args, **kwargs):
47            QMetaObject.invokeMethod(qobj, "queuedInvoke",
48                                     Qt.QueuedConnection,
49                                     Q_ARG("PyQt_PyObject",
50                                           partial(func, *args, **kwargs)))
51        return safe_wrapper
52
53
54def split_and_strip(string, sep=None):
55    return [s.strip() for s in string.split(sep)]
56
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
79class EntryGraphicsItem(QGraphicsPathItem):
80    """
81    An Graphics Item with actions for an overlay of a KEGG pathway image.
82    """
83    def __init__(self, graphics, *args):
84        QGraphicsPathItem.__init__(self, *args)
85        path = path_from_graphics(graphics)
86        self.setPath(path)
87        self.setAcceptHoverEvents(True)
88        self._actions = []
89        self.link = None
90
91    def hoverEnterEvent(self, event):
92        self.setBrush(QBrush(QColor(0, 100, 0, 100)))
93
94    def hoverLeaveEvent(self, event):
95        self.setBrush(QBrush(Qt.NoBrush))
96
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())
103
104    def itemChange(self, change, value):
105        if change == QGraphicsItem.ItemSelectedHasChanged:
106            self.setPen(QPen(Qt.red if self.isSelected() else Qt.blue, 2))
107
108        return QGraphicsPathItem.itemChange(self, change, value)
109
110
111class GraphicsPathwayItem(QGraphicsPixmapItem):
112    """
113    A Graphics Item displaying a KEGG Pathway image with optional
114    marked objects.
115
116    """
117    def __init__(self, pathway, objects, *args, **kwargs):
118        QGraphicsPixmapItem.__init__(self, *args)
119        self.setTransformationMode(Qt.SmoothTransformation)
120        self.setPathway(pathway)
121        self.setMarkedObjects(objects,
122                              name_mapper=kwargs.get("name_mapper", {}))
123
124    def setPathway(self, pathway):
125        """
126        Set pathway
127        """
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)
135
136    def setMarkedObjects(self, objects, name_mapper={}):
137        for entry in self.pathway.entries() if self.pathway else []:
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())
143            item.setToolTip(self.tooltip(entry, contained_objects,
144                                         name_mapper))
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)
150
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]
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))
164            actions.append(action)
165        elif hasattr(entry, "link"):
166            action = QAction("View %s on KEGG website" % str(type), None)
167            action.connect(action,
168                           SIGNAL("triggered()"),
169                           lambda toggled=False, address=entry.link: \
170                               webbrowser.open(address))
171            actions.append(action)
172        return actions
173
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]
177        text = entry.name[:16] + " ..." if len(entry.name) > 20 else entry.name
178        text = "<p>%s</p>" % text
179        if names:
180            text += "<br>".join(names)
181        return text
182
183    def contextMenuEvent(self, event):
184        self._menu = menu = QMenu()
185        action = menu.addAction("View this pathway on KEGG website")
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))
190        menu.popup(event.screenPos())
191
192
193class PathwayView(QGraphicsView):
194    def __init__(self, master, *args):
195        QGraphicsView.__init__(self, *args)
196        self.master = master
197
198        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
199        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
200
201        self.setRenderHints(QPainter.Antialiasing)
202        scene = QGraphicsScene(self)
203        self.pixmapGraphicsItem = QGraphicsPixmapItem(None, scene)
204        self.setScene(scene)
205
206        self.setMouseTracking(True)
207        self.viewport().setMouseTracking(True)
208
209        self.setFocusPolicy(Qt.WheelFocus)
210
211    def SetPathway(self, pathway=None, objects=[]):
212        self.scene().clear()
213        self.pathway = pathway
214        self.objects = objects
215        self.pathwayItem = GraphicsPathwayItem(
216            pathway, objects, None,
217            name_mapper=getattr(self.master, "uniqueGenesDict", {})
218        )
219
220        self.scene().addItem(self.pathwayItem)
221        self.scene().setSceneRect(self.pathwayItem.boundingRect())
222        self.updateTransform()
223
224    def resizeEvent(self, event):
225        self.updateTransform()
226        return QGraphicsView.resizeEvent(self, event)
227
228    def updateTransform(self):
229        if self.master.autoResize:
230            self.fitInView(self.scene().sceneRect().adjusted(-1, -1, 1, 1),
231                           Qt.KeepAspectRatio)
232        else:
233            self.setTransform(QTransform())
234
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)
242            painter.drawText(self.viewport().geometry(), Qt.AlignCenter,
243                             self._userMessage)
244            painter.end()
245
246
247class OWKEGGPathwayBrowser(OWWidget):
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
267    def __init__(self, parent=None, signalManager=None, name="KEGG Pathways"):
268        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
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)]
273        self.organismIndex = 0
274        self.geneAttrIndex = 0
275        self.autoCommit = False
276        self.autoResize = True
277        self.useReference = False
278        self.useAttrNames = 0
279        self.caseSensitive = True
280        self.showOrthology = True
281        self.autoFindBestOrg = False
282        self.loadSettings()
283
284        self.controlArea.setMaximumWidth(250)
285        box = OWGUI.widgetBox(self.controlArea, "Info")
286        self.infoLabel = OWGUI.widgetLabel(box, "No data on input\n")
287
288        self.allOrganismCodes = {}
289
290        self.organismCodes = []
291
292        # Organism selection.
293        box = OWGUI.widgetBox(self.controlArea, "Organism")
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
302        if self.signalManager:
303            self.signalManager.freeze(self).push()
304
305        # Selection of genes attribute
306        box = OWGUI.widgetBox(self.controlArea, "Gene attribute")
307        self.geneAttrCombo = OWGUI.comboBox(box, self, "geneAttrIndex",
308                                            callback=self.Update)
309
310        OWGUI.checkBox(box, self, "useAttrNames",
311                       "Use variable names",
312                       disables=[(-1, self.geneAttrCombo)],
313                       callback=self.UseAttrNamesCallback)
314
315        self.geneAttrCombo.setDisabled(bool(self.useAttrNames))
316
317        OWGUI.separator(self.controlArea)
318
319        OWGUI.checkBox(self.controlArea, self, "useReference",
320                       "From signal",
321                       box="Reference",
322                       callback=self.Update)
323
324        OWGUI.separator(self.controlArea)
325
326        OWGUI.checkBox(self.controlArea, self, "showOrthology",
327                       "Show pathways in full orthology",
328                       box="Orthology",
329                       callback=self.UpdateListView)
330
331        OWGUI.checkBox(self.controlArea, self, "autoResize",
332                       "Resize to fit",
333                       box="Image",
334                       callback=self.UpdatePathwayViewTransform)
335
336        box = OWGUI.widgetBox(self.controlArea, "Cache Control")
337
338        OWGUI.button(box, self, "Clear cache",
339                     callback=self.ClearCache,
340                     tooltip="Clear all locally cached KEGG data.")
341
342        OWGUI.separator(self.controlArea)
343
344        box = OWGUI.widgetBox(self.controlArea, "Selection")
345        OWGUI.checkBox(box, self, "autoCommit", "Commit on update")
346        OWGUI.button(box, self, "Commit", callback=self.Commit, default=True)
347        OWGUI.rubber(self.controlArea)
348
349        spliter = QSplitter(Qt.Vertical, self.mainArea)
350        self.pathwayView = PathwayView(self, spliter)
351        self.mainArea.layout().addWidget(spliter)
352
353        self.listView = QTreeWidget(spliter)
354        spliter.addWidget(self.listView)
355
356        self.listView.setAllColumnsShowFocus(1)
357        self.listView.setColumnCount(4)
358        self.listView.setHeaderLabels(["Pathway", "P value",
359                                       "Genes", "Reference"])
360
361        self.listView.setSelectionMode(QTreeWidget.SingleSelection)
362
363        self.listView.setSortingEnabled(True)
364
365        self.listView.setMaximumHeight(200)
366
367        self.connect(self.listView,
368                     SIGNAL("itemSelectionChanged()"),
369                     self.UpdatePathwayView)
370
371        self.connect(self.graphButton,
372                     SIGNAL("clicked()"),
373                     self.saveGraph)
374
375        self.ctrlPressed = False
376        self.selectedObjects = defaultdict(list)
377        self.data = None
378        self.refData = None
379
380        self.resize(800, 600)
381
382        self.connect(self,
383                     SIGNAL("widgetStateChanged(QString, int, QString)"),
384                     self.onStateChange)
385
386        self.has_new_data = False
387        self.has_new_reference_set = False
388
389        self.setEnabled(False)
390        QTimer.singleShot(100, self.UpdateOrganismComboBox)
391
392    def UpdateOrganismComboBox(self):
393        # First try to import slumber
394        try:
395            import slumber
396        except ImportError:
397            QMessageBox.warning(self,
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
404        try:
405            genome = obiKEGG.KEGGGenome()
406
407            self.allOrganismCodes = genome
408
409            essential = genome.essential_organisms()
410            common = genome.common_organisms()
411            common = [c for c in common if c not in essential]
412
413            # TODO: Add option to specify additional organisms not
414            # in the common list.
415
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]
431            self.organismComboBox.addItems(items)
432        finally:
433            self.setEnabled(True)
434            self.infoLabel.setText("No data on input\n")
435            if self.signalManager:
436                self.signalManager.freeze(self).pop()
437
438    def Clear(self):
439        """
440        Clear the widget state.
441        """
442        self.infoLabel.setText("No data on input\n")
443        self.listView.clear()
444        self.ClearPathway()
445
446        self.send("Selected Examples", None)
447        self.send("Unselected Examples", None)
448
449    def ClearPathway(self):
450        self.pathwayView.SetPathway(None)
451        self.selectedObjects = defaultdict(list)
452
453    def SetData(self, data=None):
454        self.closeContext()
455        self.data = data
456        self.warning(0)
457        if data is not None:
458            self.SetGeneAttrCombo()
459            taxid = data_hints.get_hint(data, "taxid", None)
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
466
467            self.useAttrNames = data_hints.get_hint(data, "genesinrows",
468                                                    self.useAttrNames)
469
470            self.openContext("", data)
471        else:
472            self.Clear()
473
474        self.has_new_data = True
475
476    def SetRefData(self, data=None):
477        self.refData = data
478        self.has_new_reference_set = True
479
480    def handleNewSignals(self):
481        if self.has_new_data or (self.has_new_reference_set and \
482                                 self.useReference):
483            self.Update()
484
485            self.has_new_data = False
486            self.has_new_reference_set = False
487
488    def UseAttrNamesCallback(self):
489        self.Update()
490
491    def OrganismSelectionCallback(self):
492        self.Update()
493
494    def SetGeneAttrCombo(self):
495        self.geneAttrCandidates = self.data.domain.variables + \
496                                  self.data.domain.getmetas().values()
497        self.geneAttrCandidates = filter(
498            lambda v: isinstance(v, (Orange.feature.Discrete,
499                                     Orange.feature.String)),
500            self.geneAttrCandidates)
501
502        self.geneAttrCombo.clear()
503
504        self.geneAttrCombo.addItems([var.name for var in
505                                     self.geneAttrCandidates])
506
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
514    def UpdateListView(self):
515        self.bestPValueItem = None
516        self.listView.clear()
517        if not self.data:
518            return
519
520        allPathways = self.org.pathways()
521        allRefPathways = obiKEGG.pathways("map")
522
523        items = []
524        self.progressBarInit()
525        kegg_pathways = obiKEGG.KEGGPathways()
526        kegg_pathways.pre_cache(self.pathways.keys(),
527                                progress_callback=self.progressBarSet)
528        self.progressBarFinished()
529
530        org_code = self.organismCodes[min(self.organismIndex,
531                                          len(self.organismCodes) - 1)]
532
533        if self.showOrthology:
534            self.koOrthology = obiKEGG.KEGGBrite("ko00001")
535            self.listView.setRootIsDecorated(True)
536            path_ids = set([s[-5:] for s in self.pathways.keys()])
537
538            def _walkCollect(koEntry):
539                num = koEntry.title[:5] if koEntry.title else None
540                if num in path_ids:
541                    return ([koEntry] +
542                            reduce(lambda li, c: li + _walkCollect(c),
543                                   [child for child in koEntry.entries],
544                                   []))
545                else:
546                    c = reduce(lambda li, c: li + _walkCollect(c),
547                               [child for child in koEntry.entries],
548                               [])
549                    return c + (c and [koEntry] or [])
550
551            allClasses = reduce(lambda li1, li2: li1 + li2,
552                                [_walkCollect(c) for c in self.koOrthology],
553                                [])
554
555            def _walkCreate(koEntry, lvItem):
556                item = QTreeWidgetItem(lvItem)
557                id = "path:" + org_code + koEntry.title[:5]
558
559                if koEntry.title[:5] in path_ids:
560                    p = kegg_pathways.get_entry(id)
561                    if p is None:
562                        # In case the genesets still have obsolete entries
563                        name = koEntry.title
564                    else:
565                        name = p.name
566                    genes, p_value, ref = self.pathways[id]
567                    item.setText(0, name)
568                    item.setText(1, "%.5f" % p_value)
569                    item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
570                    item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
571                    item.pathway_id = id if p is not None else None
572                else:
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
579                    if id in allPathways:
580                        item.pathway_id = id
581                    elif "path:map" + koEntry.title[:5] in allRefPathways:
582                        item.pathway_id = "path:map" + koEntry.title[:5]
583                    else:
584                        item.pathway_id = None
585
586                for child in koEntry.entries:
587                    if child in allClasses:
588                        _walkCreate(child, item)
589
590            for koEntry in self.koOrthology:
591                if koEntry in allClasses:
592                    _walkCreate(koEntry, self.listView)
593
594            self.listView.update()
595        else:
596            self.listView.setRootIsDecorated(False)
597            pathways = self.pathways.items()
598            pathways.sort(lambda a, b: cmp(a[1][1], b[1][1]))
599
600            for id, (genes, p_value, ref) in pathways:
601                item = QTreeWidgetItem(self.listView)
602                item.setText(0, kegg_pathways.get_entry(id).name)
603                item.setText(1, "%.5f" % p_value)
604                item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
605                item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
606                item.pathway_id = id
607                items.append(item)
608
609        self.bestPValueItem = items and items[0] or None
610        self.listView.expandAll()
611        for i in range(4):
612            self.listView.resizeColumnToContents(i)
613
614        if self.bestPValueItem:
615            index = self.listView.indexFromItem(self.bestPValueItem)
616            self.listView.selectionModel().select(
617                index, QItemSelectionModel.ClearAndSelect
618            )
619
620    def UpdatePathwayView(self):
621        items = self.listView.selectedItems()
622
623        if len(items) > 0:
624            item = items[0]
625        else:
626            item = None
627
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
634
635        if USE_THREADING:
636            result = {}
637
638            def call(pathway_id):
639                result["pathway"] = p = obiKEGG.KEGGPathway(pathway_id)
640                p._get_kgml()  # makes sure the kgml file is downloaded
641                p._get_image_filename()  # makes sure the image is downloaded
642
643            self.setEnabled(False)
644            try:
645                thread = threading.Thread(None, call,
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)
660
661        self.pathwayView.SetPathway(self.pathway,
662                                    self.pathways.get(item.pathway_id, [[]])[0])
663
664    def UpdatePathwayViewTransform(self):
665        self.pathwayView.updateTransform()
666
667    def Update(self):
668        if not self.data:
669            return
670        self.error(0)
671        self.information(0)
672        pb = OWGUI.ProgressBar(self, 100)
673        if self.useAttrNames:
674            genes = [str(v.name).strip() for v in self.data.domain.attributes]
675        elif self.geneAttrCandidates:
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()]
680            if any("," in gene for gene in genes):
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.")
687        else:
688            self.error(0, "Cannot extract gene names from input")
689            genes = []
690        org_code = self.organismCodes[min(self.organismIndex,
691                                          len(self.organismCodes) - 1)]
692
693        if USE_THREADING:
694            result = {}
695
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()
700
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)
712
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)
719
720        uniqueGenes, _, _ = org.get_unique_gene_ids(set(genes),
721                                                    self.caseSensitive)
722        genesCount = len(set(genes))
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
728        self.information(1)
729        if self.useReference and self.refData:
730            if self.useAttrNames:
731                reference = [str(v.name).strip() for v in self.refData]
732            else:
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()]
737                if any("," in gene for gene in reference):
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)
747            self.referenceGenes = reference = uniqueRefGenes.keys()
748        else:
749            self.referenceGenes = reference = org.get_genes()
750        self.uniqueGenesDict = uniqueGenes
751        self.genes = uniqueGenes.keys()
752        self.revUniqueGenesDict = dict([(val, key) for key, val in
753                                        self.uniqueGenesDict.items()])
754
755        taxid = obiKEGG.to_taxid(org.org_code)
756        r_tax_map = dict((v, k) for k, v in
757                         obiKEGG.KEGGGenome.TAXID_MAP.items())
758        if taxid in r_tax_map:
759            taxid = r_tax_map[taxid]
760
761        with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
762            orngServerFiles.update(obiGeneSets.sfdomain, "index.pck")
763            kegg_gs_collections = \
764                list(obiGeneSets.collections((("KEGG", "pathways"), taxid)))
765
766        if USE_THREADING:
767            result = {}
768
769            def callable(*args, **kwargs):
770#                result["result"] = org.get_enriched_pathways(*args, **kwargs)
771                result["result"] = pathway_enrichment(*args, **kwargs)
772
773            self.setEnabled(False)
774            try:
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
787                thread.start()
788                while thread.is_alive():
789                    thread.join(timeout=0.025)
790                    qApp.processEvents()
791            finally:
792                self.setEnabled(True)
793
794            if "result" in result:
795                self.pathways = result["result"]
796            else:
797                raise Exception('Could not get enriched pathways')
798
799        else:
800            self.pathways = org.get_enriched_pathways(
801                self.genes, reference,
802                callback=self.progressBarSet
803            )
804
805        self.org = org
806        if not self.pathways:
807            self.warning(0, "No enriched pathways found.")
808        else:
809            self.warning(0)
810
811        self.UpdateListView()
812        pb.finish()
813
814    def Commit(self):
815        if self.data:
816            selectedItems = self.pathwayView.scene().selectedItems()
817            selectedGenes = reduce(set.union, [item.marked_objects
818                                               for item in selectedItems],
819                                   set())
820
821            if self.useAttrNames:
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)
826                self.send("Selected Examples", data)
827            elif self.geneAttrCandidates:
828                geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
829                                                       len(self.geneAttrCandidates) - 1)]
830                selectedExamples = []
831                otherExamples = []
832                for ex in self.data:
833                    names = [self.revUniqueGenesDict.get(name, None)
834                             for name in split_and_strip(str(ex[geneAttr]), ",")]
835                    if any(name and name in selectedGenes for name in names):
836                        selectedExamples.append(ex)
837                    else:
838                        otherExamples.append(ex)
839
840                if selectedExamples:
841                    selectedExamples = Orange.data.Table(selectedExamples)
842                else:
843                    selectedExamples = None
844
845                if otherExamples:
846                    otherExamples = Orange.data.Table(otherExamples)
847                else:
848                    otherExamples = None
849
850                self.send("Selected Examples", selectedExamples)
851                self.send("Unselected Examples", otherExamples)
852        else:
853            self.send("Selected Examples", None)
854            self.send("Unselected Examples", None)
855
856    def ClearCache(self):
857        from ..obiKEGG import caching
858        try:
859            caching.clear_cache()
860        except Exception, ex:
861            QMessageBox.warning(self, "Cache clear", ex.args[0])
862
863    def onStateChange(self, stateType, id, text):
864        if stateType == "Warning":
865            self.pathwayView._userMessage = text
866            self.pathwayView.viewport().update()
867
868    def saveGraph(self):
869        from Orange.OrangeWidgets.OWDlgs import OWChooseImageSizeDlg
870        sizeDlg = OWChooseImageSizeDlg(self.pathwayView.scene(), parent=self)
871        sizeDlg.exec_()
872
873    @pyqtSignature("queuedInvoke(PyQt_PyObject)")
874    def queuedInvoke(self, func):
875        func()
876
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
884
885    def onDeleteWidget(self):
886        """
887        Called before the widget is removed from the canvas.
888        """
889        self.org = None
890        gc.collect()  # Force collection
891
892
893from .. import obiProb
894
895
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()
901
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:
914            callback(100.0 * i / len(genesets))
915
916    # FDR correction
917    p_values = obiProb.FDR(p_values)
918
919    return dict([(id, (genes, p_val, len(ref)))
920                 for (id, genes, ref), p_val in zip(result_sets, p_values)])
921
922
923if __name__ == "__main__":
924    app = QApplication(sys.argv)
925    data = Orange.data.Table("brown-selected.tab")
926    w = OWKEGGPathwayBrowser()
927    w.UpdateOrganismComboBox()
928    w.show()
929    w.SetData(Orange.data.Table(data[:]))
930    QTimer.singleShot(10, w.handleNewSignals)
931
932    app.exec_()
933    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.