source: orange-bioinformatics/orangecontrib/bio/widgets/OWKEGGPathwayBrowser.py @ 1874:b3e32cc5cf6f

Revision 1874:b3e32cc5cf6f, 34.0 KB checked in by Ales Erjavec <ales.erjavec@…>, 6 months ago (diff)

Added new style widget meta descriptions.

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