source: orange-bioinformatics/widgets/OWGeneInfo.py @ 1272:c9926ac6d345

Revision 1272:c9926ac6d345, 15.1 KB checked in by ales_erjavec <ales.erjavec@…>, 3 years ago (diff)
  • reworked the OWConcurrent API
  • added helper methods in OWBaseWidget for concurrency
  • signalManager is aware of blocking widgets, but for the moment this is useless
  • reworked the signal managers signal "freeze" functionality (signal processing can be frozen at any time in the GUI by a new Pause-Play button)
  • added Dynamic output signals (for now in use only by "Load model" and "Python Script" widget)
Line 
1"""
2<name>Gene Info</name>
3<description>Displays gene information from NCBI.</description>
4<priority>2010</priority>
5<contact>Ales Erjavec (ales.erjevec(@at@)fri.uni-lj.si)</contact>
6<icon>icons/GeneInfo.png</icon>
7"""
8from __future__ import with_statement
9
10import obiGene, obiTaxonomy
11import orange
12import orngServerFiles
13
14from OWWidget import *
15import OWGUI
16
17from orngDataCaching import data_hints
18
19from collections import defaultdict
20from functools import partial
21
22class TreeModel(QAbstractItemModel):
23    def __init__(self, data, header, parent):
24        QAbstractItemModel.__init__(self, parent)
25        self._data = [[QVariant(s) for s in row] for row in data]
26        self._dataDict = {}
27        self._header = header
28        self._roleData = {Qt.DisplayRole:self._data}
29        self._roleData = partial(defaultdict, partial(defaultdict, partial(defaultdict, QVariant)))(self._roleData)
30   
31    def setColumnLinks(self, column, links):
32        font =QFont()
33        font.setUnderline(True)
34        font = QVariant(font)
35        for i, link in enumerate(links):
36            self._roleData[LinkRole][i][column] = QVariant(link)
37            self._roleData[Qt.FontRole][i][column] = font
38            self._roleData[Qt.ForegroundRole][i][column] = QVariant(QColor(Qt.blue))
39   
40    def setRoleData(self, role, row, col, data):
41        self._roleData[role][row][col] = data
42       
43    def data(self, index, role):
44        row, col = index.row(), index.column()
45        return self._roleData[role][row][col]
46       
47    def index(self, row, col, parent=QModelIndex()):
48        return self.createIndex(row, col, 0)
49   
50    def parent(self, index):
51        return QModelIndex()
52   
53    def rowCount(self, index):
54        if index.isValid():
55            return 0
56        else:
57            return len(self._data)
58       
59    def columnCount(self, index):
60        return len(self._header)
61
62    def headerData(self, section, orientation, role):
63        if role==Qt.DisplayRole:
64            return QVariant(self._header[section])
65        return QVariant()
66
67from OWGUI import LinkStyledItemDelegate, LinkRole
68       
69class OWGeneInfo(OWWidget):
70    settingsList = ["organismIndex", "geneAttr", "useAttr", "autoCommit"]
71    contextHandlers = {"":DomainContextHandler("", ["organismIndex", "geneAttr", "useAttr"])}
72    def __init__(self, parent=None, signalManager=None, name="Gene Info"):
73        OWWidget.__init__(self, parent, signalManager, name)
74
75        self.inputs = [("Examples", ExampleTable, self.setData)]
76        self.outputs = [("Selected Examples", ExampleTable)]
77
78        self.organismIndex = 0
79        self.geneAttr = 0
80        self.useAttr = False
81        self.autoCommit = False
82        self.searchString = ""
83        self.selectionChangedFlag = False
84        self.loadSettings()
85       
86        self.infoLabel = OWGUI.widgetLabel(OWGUI.widgetBox(self.controlArea, "Info", addSpace=True), "No data on input\n")
87        self.organisms = sorted(set([name.split(".")[-2] for name in orngServerFiles.listfiles("NCBI_geneinfo")] + obiGene.NCBIGeneInfo.essential_taxids()))
88   
89        self.orgaismsComboBox = OWGUI.comboBox(self.controlArea, self, "organismIndex", "Organism", items=[obiTaxonomy.name(id) for id in self.organisms], callback=self.setItems, debuggingEnabled=0)
90        OWGUI.separator(self.controlArea)
91        box = OWGUI.widgetBox(self.controlArea, "Gene names", addSpace=True)
92        self.geneAttrComboBox = OWGUI.comboBox(box, self, "geneAttr", "Gene atttibute", callback=self.setItems)
93        c = OWGUI.checkBox(box, self, "useAttr", "Use attribute names", callback=self.setItems, disables=[(-1, self.geneAttrComboBox)])
94        self.geneAttrComboBox.setDisabled(bool(self.useAttr))
95
96        box = OWGUI.widgetBox(self.controlArea, "Commit", addSpace=True)
97        b = OWGUI.button(box, self, "Commit", callback=self.commit)
98        c = OWGUI.checkBox(box, self, "autoCommit", "Commit on change")
99        OWGUI.setStopper(self, b, c, "selectionChangedFlag", callback=self.commit)
100       
101        ##A label for dictyExpress link
102        self.dictyExpressBox = OWGUI.widgetBox(self.controlArea, "Dicty Express")
103        self.linkLabel = OWGUI.widgetLabel(self.dictyExpressBox, "")
104        self.linkLabel.setOpenExternalLinks(True)
105        self.dictyExpressBox.hide()
106       
107        OWGUI.rubber(self.controlArea)
108
109        OWGUI.lineEdit(self.mainArea, self, "searchString", "Filter", callbackOnType=True, callback=self.searchUpdate)
110#        self.treeWidget = QTreeWidget(self.mainArea)
111        self.treeWidget = QTreeView(self.mainArea)
112        #self.treeWidget.setHeaderLabels(["NCBI ID", "Symbol", "Locus Tag", "Chromosome", "Description", "Synonyms", "Nomenclature"])
113        self.treeWidget.setRootIsDecorated(False)
114        self.treeWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
115        self.treeWidget.setItemDelegate(LinkStyledItemDelegate(self.treeWidget))
116        #self.connect(self.treeWidget, SIGNAL("itemSelectionChanged()"), self.commitIf)
117        self.treeWidget.viewport().setMouseTracking(True)
118        self.treeWidget.setSortingEnabled(True)
119        self.mainArea.layout().addWidget(self.treeWidget)
120       
121        box = OWGUI.widgetBox(self.mainArea, "", orientation="horizontal")
122        OWGUI.button(box, self, "Select Filtered", callback=self.selectFiltered)
123        OWGUI.button(box, self, "Clear Selection", callback=self.treeWidget.clearSelection)
124       
125        self.resize(1000, 700)       
126
127        self.geneinfo = []
128        self.cells = []
129        self.data = None
130        self.currentLoaded = None, None
131        self.selectionUpdateInProgress = False
132       
133    def setData(self, data=None):
134        self.closeContext()
135        self.data = data
136        if data:
137            self.geneAttrComboBox.clear()
138            self.attributes = [attr for attr in self.data.domain.variables + self.data.domain.getmetas().values() if attr.varType in [orange.VarTypes.String, orange.VarTypes.Discrete]]
139            self.geneAttrComboBox.addItems([attr.name for attr in self.attributes])
140            self.openContext("", data)
141            self.geneAttr = min(self.geneAttr, len(self.attributes) - 1)
142           
143            taxid = data_hints.get_hint(self.data, "taxid", "")
144            if taxid in self.organisms:
145                self.organismIndex = self.organisms.index(taxid)
146               
147            self.useAttr = data_hints.get_hint(self.data, "genesinrows",  self.useAttr)
148           
149            self.setItems()
150        else:
151            self.clear()
152
153    def setItems(self):
154        self.warning(0)
155        if not self.data:
156            return
157        if self.useAttr:
158            genes = [attr.name for attr in self.data.domain.attributes]
159        elif self.attributes:
160            attr = self.attributes[self.geneAttr]
161            genes = [str(ex[attr]) for ex in self.data if not ex[attr].isSpecial()]
162        else:
163            genes = []
164        if not genes:
165            self.warning(0, "Could not extract genes from input dataset.")
166        self.warning(1)
167        org = self.organisms[min(self.organismIndex, len(self.organisms) - 1)]
168        info , currorg = self.currentLoaded
169        self.error(0)
170        if currorg != org:
171            self.progressBarInit()
172           
173            ## Load the gene info in a worker thread
174            call = self.asyncCall(obiGene.NCBIGeneInfo, (org,), name="Loading NCBI Gene info", blocking=False)
175            call.connect(call, SIGNAL("progressChanged(float)"), self.progressBarSet, Qt.QueuedConnection)
176            with orngServerFiles.DownloadProgress.setredirect(call.emitProgressChanged):
177                call.__call__()
178                info = call.get_result()
179
180#            with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
181#                info = obiGene.NCBIGeneInfo(self.organisms[min(self.organismIndex, len(self.organisms) - 1)])
182            self.progressBarFinished()
183            self.currentLoaded = info, org
184           
185        self.updateDictyExpressLink(genes, show=org == "352472")
186           
187        self.geneinfo = geneinfo = [(gene, info.get_info(gene, None)) for gene in genes]
188
189        self.progressBarInit()
190        milestones = set([i for i in range(0, len(geneinfo), max(len(geneinfo)/100, 1))])
191        self.cells = cells = []
192        links = []
193        for i, (gene, gi) in enumerate(geneinfo):
194            if gi:
195                cells.append([gi.gene_id, gi.symbol + " (%s)" % gene if gene != gi.symbol else gi.symbol,
196                            gi.locus_tag or "", gi.chromosome or "", gi.description or "",
197                            ", ".join(gi.synonyms), gi.symbol_from_nomenclature_authority or ""])
198                links.append("http://www.ncbi.nlm.nih.gov/sites/entrez?Db=gene&Cmd=ShowDetailView&TermToSearch=%s" % gi.gene_id)
199
200            if i in milestones:
201                self.progressBarSet(100.0*i/len(geneinfo))
202        model = TreeModel(cells, ["NCBI ID", "Symbol", "Locus Tag", "Chromosome", "Description", "Synonyms", "Nomenclature"], self.treeWidget)
203        model.setColumnLinks(0, links)
204        proxyModel = QSortFilterProxyModel(self)
205        proxyModel.setSourceModel(model)
206        self.treeWidget.setModel(proxyModel)
207        self.connect(self.treeWidget.selectionModel(), SIGNAL("selectionChanged(QItemSelection , QItemSelection )"), self.commitIf)
208        for i in range(7):
209            self.treeWidget.resizeColumnToContents(i)
210            self.treeWidget.setColumnWidth(i, min(self.treeWidget.columnWidth(i), 200))
211        self.treeWidget.update()
212        self.progressBarFinished()
213
214        self.infoLabel.setText("%i genes\n%i matched NCBI's IDs" % (len(genes), len(cells)))
215        self.matchedInfo = len(genes), len(cells)
216
217    def clear(self):
218        self.infoLabel.setText("No data on input\n")
219        self.treeWidget.setModel(TreeModel([], ["NCBI ID", "Symbol", "Locus Tag", "Chromosome", "Description", "Synonyms", "Nomenclature"], self.treeWidget))
220        self.geneAttrComboBox.clear()
221        self.send("Selected Examples", None)
222
223    def commitIf(self, *args):
224        if self.autoCommit and not self.selectionUpdateInProgress:
225            self.commit()
226        else:
227            self.selectionChangedFlag = True
228
229    def commit(self):
230        if not self.data:
231            return
232       
233        mapToSource = self.treeWidget.model().mapToSource
234        selectedIds = [self.cells[mapToSource(index).row()][0] for index in self.treeWidget.selectedIndexes()]
235       
236        selected = [gi for gene, gi in self.geneinfo if gi and gi.gene_id in selectedIds]
237        if self.useAttr:
238            attrs = [attr for attr, (name, gi) in zip(self.data.domain.attributes, self.geneinfo) if gi in selected]
239            domain = orange.Domain(attrs, self.data.domain.classVar)
240            domain.addmetas(self.data.domain.getmetas())
241            newdata = orange.ExampleTable(domain, self.data)
242            self.send("Selected Examples", newdata)
243        else:
244            attr = self.attributes[self.geneAttr]
245            geneinfo = dict(self.geneinfo)
246            examples = [ex for ex in self.data if geneinfo.get(str(ex[attr])) in selected]
247            if examples:
248                newdata = orange.ExampleTable(examples)
249            else:
250                newdata = None
251            self.send("Selected Examples", newdata)
252           
253    def rowFiltered(self, row):
254        searchStrings = self.searchString.lower().split()
255        row = unicode(" ".join(self.cells[row]).lower(), errors="ignore")
256        return not all([s in row for s in searchStrings])
257   
258    def searchUpdate(self):
259        if not self.data:
260            return
261        searchStrings = self.searchString.lower().split()
262        index = self.treeWidget.model().sourceModel().index
263        mapFromSource = self.treeWidget.model().mapFromSource
264        for i, row in enumerate(self.cells):
265            row = unicode(" ".join(row).lower(), errors="ignore")
266            self.treeWidget.setRowHidden(mapFromSource(index(i, 0)).row(), QModelIndex(), not all([s in row for s in searchStrings]))
267        #self.treeWidget.model().setFilterRegExp(QRegExp(self.searchString, Qt.CaseInsensitive, QRegExp.FixedString))
268           
269    def selectFiltered(self):
270        if not self.data:
271            return
272        itemSelection = QItemSelection()
273       
274        index = self.treeWidget.model().sourceModel().index
275        mapFromSource = self.treeWidget.model().mapFromSource
276        for i, row in enumerate(self.cells):
277            if not self.rowFiltered(i):
278                itemSelection.select(mapFromSource(index(i, 0)), mapFromSource(index(i, 0)))
279        self.treeWidget.selectionModel().select(itemSelection, QItemSelectionModel.Select | QItemSelectionModel.Rows)
280       
281    def sendReport(self):
282        import OWReport
283        genes, matched = self.matchedInfo
284        info, org = self.currentLoaded
285        self.reportRaw("<p>Input: %i genes of which %i (%.1f%%) matched NCBI synonyms<br>Organism: %s<br>Filter: %s</p>" % (genes, matched, 100.0 * matched / genes, obiTaxonomy.name(org), self.searchString))
286        self.reportSubsection("Gene list")
287        self.reportRaw(reportItemView(self.treeWidget))
288       
289    def updateDictyExpressLink(self, genes, show=False):
290        def fix(ddb):
291            if ddb.startswith("DDB"): 
292                if not ddb.startswith("DDB_G"):
293                    ddb = ddb.replace("DDB", "DDB_G")
294                return ddb
295            return None 
296        if show:
297            genes = [fix(gene) for gene in genes if fix(gene)]
298            link1 = '<a href="http://www.ailab.si/dictyexpress/run/index.php?gene=%s">Microarray profile</a>' % (" ".join(genes))
299            link2 = '<a href="http://www.ailab.si/dictyexpress/run/index.php?gene=%s&db=rnaseq">RNA-Seq profile</a>' % (" ".join(genes))
300            self.linkLabel.setText(link1 + "<br/>" + link2)
301           
302            show = any(genes)
303               
304        if show:
305            self.dictyExpressBox.show()
306        else:
307            self.dictyExpressBox.hide()
308       
309def reportItemView(view):
310    model = view.model()
311    return reportItemModel(view, model)
312   
313def reportItemModel(view, model, index=QModelIndex()):
314    if not index.isValid() or model.hasChildren(index):
315        columnCount, rowCount = model.columnCount(index), model.rowCount(index)
316        if not index.isValid():
317            text = '<table>\n<tr>' + ''.join('<th>%s</th>' % model.headerData(i, Qt.Horizontal, Qt.DisplayRole).toString() for i in range(columnCount)) +'</tr>\n'
318        else:
319#            variant = model.data(index, Qt.DisplayRole)
320#            text = '<table' + (' caption="%s"' % variant.toString() if variant.isValid() else '') + '>\n'
321            pass
322        text += ''.join('<tr>' + ''.join('<td>' + reportItemModel(view, model, model.index(row, column, index)) + '</td>' for column in range(columnCount)) + '</tr>\n' for row in range(rowCount) if not view.isRowHidden(row, index))
323        text += '</table>'
324        return text
325    else:
326        variant = model.data(index, Qt.DisplayRole)
327        return str(variant.toString()) if variant.isValid() else ""
328       
329if __name__ == "__main__":
330    app = QApplication(sys.argv)
331    data = orange.ExampleTable("../../orange/doc/datasets/brown-selected.tab")
332    w = OWGeneInfo()
333    w.show()
334    w.setData(data)
335    app.exec_()
336    w.saveSettings()
337       
338       
339       
340       
Note: See TracBrowser for help on using the repository browser.