source: orange-bioinformatics/orangecontrib/bio/widgets/OWMeSHBrowser.py @ 1994:633673fab61f

Revision 1994:633673fab61f, 17.7 KB checked in by Ales Erjavec <ales.erjavec@…>, 8 weeks ago (diff)

Normalized whitespace (and other pep8 fixes).

Line 
1"""
2<name>MeSH Browser</name>
3<description>Browse MeSH ontology.</description>
4<icon>icons/MeSHBrowser.svg</icon>
5<contact>Crt Gorup (crt.gorup@gmail.com)</contact> 
6<priority>2040</priority>
7"""
8
9from __future__ import absolute_import
10
11from PyQt4.QtCore import *
12from PyQt4.QtGui import *
13
14from Orange.OrangeWidgets import OWGUI
15from Orange.OrangeWidgets.OWWidget import *
16
17from ..obiMeSH import *
18
19NAME = "MeSH Browser"
20DESCRIPTION = "Browse MeSH ontology."
21ICON = "icons/MeSHBrowser.svg"
22PRIORITY = 2040
23
24INPUTS = [("Reference data", Orange.data.Table, "getReferenceData"),
25          ("Cluster data", Orange.data.Table, "getClusterData")]
26
27OUTPUTS = [("Selected examples", Orange.data.Table)]
28
29REPLACES = ["_bioinformatics.widgets.OWMeSHBrowser.OWMeSHBrowser"]
30
31
32class MyQTableWidgetItem(QTableWidgetItem):
33
34    """ Our implementation of QTable item allowing numerical sorting.  """
35
36    def __init__(self, table, text):
37        QTableWidgetItem.__init__(self, table, QTableWidgetItem.Never, text)
38        self.data = text
39
40    def key(self):    # additional setting to correctly handle text and numerical sorting
41        try:    # sorting numerical column
42            e = float(self.data)
43            tdata = pval = "%.4g" % e
44            l = len(self.data)
45            pre = ""
46
47            offset = 0  # additional parameter to hadle exponent '2e-14' float format
48            if(tdata.count('e') > 0 or tdata.count('E') > 0):
49                pre = "*"
50                offset = 1
51
52            for i in range(0, 40 - l - offset):
53                pre = pre + "0"
54            return pre + tdata
55        except ValueError:    # sorting text column
56            return self.data
57
58
59class ListViewToolTip(QToolTip):
60
61    """brief A class to allow tooltips in a listview."""
62
63    def __init__(self, view, column, data):
64        """brief ListViewToolTip constructor.
65        \param view       QListView instance
66        \param column       Listview column
67        \param truncatedOnly Only display the tooltip if the column data is truncated
68        """
69        QToolTip.__init__(self, view.viewport())
70        self.__view = view
71        self.__col = column
72        self.__data = data    # mapping from name -> description
73        # self.setWakeUpDelay(400)
74
75    def appendData(self, data):
76        self.__data = data
77
78    def maybeTip(self, pos):
79        """brief Draw the tooltip.
80        \param pos Tooltip position.
81        """
82        item = self.__view.itemAt(pos)
83        if item is not None:
84            if(self.__data.has_key(str(item.text(self.__col)))):
85                tipString = self.__data[str(item.text(self.__col))]
86                counter = 45
87                newTipString = ""
88                for i in tipString.split(" "):
89                    if counter < 0:
90                        newTipString = newTipString + i + "\n"
91                        counter = 45
92                    else:
93                        newTipString = newTipString + i + " "
94                        counter -= len(i)
95                tipString = newTipString
96                cr = self.__view.itemRect(item)
97                headerPos = self.__view.header().sectionPos(self.__col)
98                cr.setLeft(headerPos)
99                cr.setRight(headerPos + self.__view.header().sectionSize(self.__col))
100                self.tip(cr, tipString)
101            else:
102                print item.text(self.__col) + " not found in toDecs"
103
104
105class OWMeSHBrowser(OWWidget):
106    settingsList = ["multi", "maxPValue", "minExamplesInTerm"]
107
108    def __init__(self, parent=None, signalManager=None):
109        OWWidget.__init__(self, parent, signalManager, "MeshBrowser")
110        self.inputs = [("Reference data", ExampleTable, self.getReferenceData), ("Cluster data", ExampleTable, self.getClusterData)]
111        self.outputs = [("Selected examples", ExampleTable)]
112
113        # widget variables
114        self.loadedRef = 0
115        self.loadedClu = 0
116        self.maxPValue = 0.05
117        self.minExamplesInTerm = 5
118        self.multi = 1
119        self.reference = None
120        self.cluster = None
121        self.loadSettings()
122        self.mesh = obiMeSH() # main object is created
123        self.dataLoaded = self.mesh.dataLoaded
124
125        # left pane
126        box = OWGUI.widgetBox(self.controlArea, "Info")
127        #box = QGroupBox("Info", self.controlArea)
128        self.infoa = OWGUI.label(box, self, "No reference data.")
129        self.infob = OWGUI.label(box, self, "No cluster data.")
130        self.ratio = OWGUI.label(box, self, "")
131        self.ref_att = OWGUI.label(box, self, "")
132        self.clu_att = OWGUI.label(box, self, "")
133        self.resize(960, 600)
134        OWGUI.separator(self.controlArea)
135
136        self.optionsBox = OWGUI.widgetBox(self.controlArea, "Options")
137        self.maxp = OWGUI.lineEdit(self.optionsBox, self, "maxPValue", label="threshold:", orientation="horizontal", labelWidth=120, valueType=float)
138        self.minf = OWGUI.lineEdit(self.optionsBox, self, "minExamplesInTerm", label="min. frequency:", orientation="horizontal", labelWidth=120, valueType=int)
139        #OWGUI.checkBox(self.optionsBox, self, 'multi', 'Multiple selection', callback= self.checkClicked)
140        OWGUI.button(self.optionsBox, self, "Refresh", callback=self.refresh)
141
142        # right pane
143        self.col_size = [280, 84, 84, 100, 110]
144        self.sort_col = 0
145        self.sort_dir = True
146        self.columns = ['MeSH term', '# reference', '# cluster', 'p value', 'fold enrichment'] # both datasets
147
148        self.splitter = QSplitter(Qt.Vertical, self.mainArea)
149        self.mainArea.layout().addWidget(self.splitter)
150
151        # list view
152        self.meshLV = QTreeWidget(self.splitter)
153        #self.meshLV.setSelectionMode(QAbstractItemView.MultiSelection)
154        self.meshLV.setAllColumnsShowFocus(1)
155        self.meshLV.setColumnCount(len(self.columns))
156        self.meshLV.setHeaderLabels(self.columns)
157
158        self.meshLV.header().setClickable(True)
159        #self.meshLV.header().setSortIndicatorShown(True)
160        #self.meshLV.setSortingEnabled(True)
161        self.meshLV.setRootIsDecorated(True)
162        self.connect(self.meshLV, SIGNAL("itemSelectionChanged()"), self.viewSelectionChanged)
163        #self.meshLV.setItemDelegateForColumn(3, EnrichmentColumnItemDelegate(self))
164        #self.tooltips = ListViewToolTip(self.meshLV,0, self.mesh.toDesc)
165
166        # table of significant mesh terms
167        self.sigTermsTable = QTableWidget(self.splitter)
168        self.sigTermsTable.setColumnCount(len(self.columns))
169        self.sigTermsTable.setRowCount(4)
170        ## hide the vertical header
171        self.sigTermsTable.verticalHeader().hide()
172        #self.sigTermsTable.setLeftMargin(0)
173        #self.sigTermsTable.setSelectionMode(QAbstractItemView.MultiSelection)
174
175        for i in range(0, len(self.columns)):
176            self.sigTermsTable.horizontalHeader().resizeSection(i, self.col_size[i])
177            self.meshLV.header().resizeSection(i, self.col_size[i])
178
179        self.sigTermsTable.setHorizontalHeaderLabels(self.columns)
180
181        self.connect(self.sigTermsTable, SIGNAL("itemSelectionChanged()"), self.tableSelectionChanged)
182        self.connect(self.sigTermsTable, SIGNAL("clicked(int,int,int,const QPoint&)"), self.tableClicked)
183        self.splitter.show()
184        self.optionsBox.setDisabled(1)
185
186    def tableSelectionChanged(self):
187        return True
188
189    def tableClicked(self, row, col, button, point):
190        if self.sort_col == col:
191            self.sort_dir = not self.sort_dir
192        else:
193            self.sort_col = col
194            self.sort_dir = True
195
196        self.sigTermsTable.sortItems(self.sort_col, Qt.DescendingOrder)
197        #print "sortiram ", col, " ",row
198
199    def checkClicked(self):
200        if self.multi == 0:
201            self.meshLV.clearSelection()
202        self.meshLV.setSelectionMode(QAbstractItemView.MultiSelection)
203
204    def viewSelectionChanged(self):
205        """
206        Function viewSelectionChanged is used to handle the widget output once the user clicks or selects MeSH term inside the tree view.
207        """
208        items = list()
209        self.progressBarInit()
210
211        itms = self.meshLV.selectedItems()
212        for i in itms:
213            items.append(i.term)
214
215        #for i in self.lvItem2Mesh.iterkeys():
216        #   if i.isSelected():
217        #       items.append(self.lvItem2Mesh[i][1])
218        #print "selecting ", items
219
220        if self.reference:
221            data = self.mesh.findSubset(self.reference, items, callback=self.progressBarSet, MeSHtype='term')
222        else:
223            data = self.mesh.findSubset(self.cluster, items, callback=self.progressBarSet, MeSHtype='term')
224
225        #print items
226        self.send("Selected examples", data)
227        self.progressBarFinished()
228
229    def __updateData__(self):
230        """
231        Function __updateData__ is used to display the results of the MeSH term enrichment analysis inside the widget components.
232        """
233        self.lvItem2Mesh = dict()
234
235        if(self.reference and self.cluster):
236            if(len(self.cluster) > len(self.reference)):
237                self.optionsBox.setDisabled(1)
238                return False
239            # everything is ok, now we can calculate enrichment and update labels, tree view and table data
240            #self.warning()
241            self.optionsBox.setDisabled(0)
242            self.progressBarInit()
243            self.treeInfo, self.results = self.mesh.findEnrichedTerms(self.reference, self.cluster, self.maxPValue, treeData= True, callback= self.progressBarSet)
244            self.progressBarFinished()
245            self.ratio.setText("ratio = %.4g" % self.mesh.ratio)
246            self.clu_att.setText("cluster MeSH att: " + self.mesh.clu_att)
247            self.ref_att.setText("reference MeSH att: " + self.mesh.ref_att)
248
249            # table data update
250            self.sigTermsTable.setRowCount(len(self.results))
251            index = 0
252            for i in self.results.iterkeys(): ## sorted by the p value
253               # mTerm = i[0]
254                mID = self.mesh.toName[i] + " (" + i + ")"
255                rF = self.results[i][0]
256                cF = self.results[i][1]
257                pval = self.results[i][2]
258                fold = self.results[i][3]
259                pval = "%.4g" % pval
260                fold = "%.4g" % fold
261                vals = [mID, rF, cF, pval, fold]
262                for j in range(len(vals)):
263                    self.sigTermsTable.setItem(index, j, QTableWidgetItem(str(vals[j])))
264                index = index + 1
265
266            # initial sorting - p value
267            self.sort_col = 3
268            self.sort_dir = True
269            self.sigTermsTable.sortItems(self.sort_col, Qt.DescendingOrder)
270
271            # tree view update - The most beautiful part of this widget!
272            starters = self.treeInfo["tops"]       # we get a list of possible top nodes
273            self.meshLV.clear()
274
275            for e in starters:    # we manualy create top nodes
276                f = QTreeWidgetItem(self.meshLV);
277                #f.setOpen(1)
278                rfr = str(self.results[e][0])
279                cfr = str(self.results[e][1])
280                pval = "%.4g" % self.results[e][2]
281                fold = "%.4g" % self.results[e][3]
282                self.lvItem2Mesh[f] = (self.mesh.toName[e], e)
283                data = [self.mesh.toName[e], rfr, cfr, pval, fold]
284                f.term = self.mesh.toName[e]
285                for t in range(len(data)):
286                    f.setText(t, data[t])
287                self.__treeViewMaker__(f, e, False)
288            self.meshLV.expandAll()
289
290        elif self.reference or self.cluster:
291            if self.reference:
292                current_data = self.reference
293            else:
294                current_data = self.cluster
295
296            self.optionsBox.setDisabled(0)
297            self.progressBarInit()
298            self.treeInfo, self.results = self.mesh.findFrequentTerms(current_data, self.minExamplesInTerm, treeData=True, callback=self.progressBarSet)
299            self.progressBarFinished()
300            if self.reference:
301                self.ref_att.setText("reference MeSH att: " + self.mesh.solo_att)
302            else:
303                self.clu_att.setText("cluster MeSH att: " + self.mesh.solo_att)
304
305            # table data update
306            self.sigTermsTable.setRowCount(len(self.results))
307            index = 0
308            for i in self.results.iterkeys():
309                mID = self.mesh.toName[i] + " (" + i + ")"
310                rF = self.results[i]
311                vals = [mID, rF]
312                for j in range(len(vals)):
313                    self.sigTermsTable.setItem(index, j, QTableWidgetItem(str(vals[j])))
314                index = index + 1
315
316            # initial sorting - frequency
317            self.sort_col = 1
318            self.sort_dir = True
319            self.sigTermsTable.sortItems(self.sort_col, Qt.DescendingOrder)
320
321            # tree view update - The most beautiful part of this widget!
322            starters = self.treeInfo["tops"]       # we get a list of possible top nodes
323            self.meshLV.clear()
324
325            for e in starters:    # we manualy create top nodes
326                f = QTreeWidgetItem(self.meshLV);
327                #f.setOpen(1)
328                self.lvItem2Mesh[f] = (self.mesh.toName[e], e)
329                rfr = str(self.results[e])
330                data = [self.mesh.toName[e], rfr]
331                f.term = self.mesh.toName[e]
332                for t in range(len(data)):
333                    f.setText(t, data[t])
334                self.__treeViewMaker__(f, e, True)
335            self.meshLV.expandAll()
336
337    def __treeViewMaker__(self, parentLVI, parentID, soloMode):
338        """
339        Function __treeViewMaker__ is used to build the tree in treeListView. When soloMode=True function only displays \
340        first two columns inside the tree view (suitable when only one dataset is present).
341        """
342        for i in self.treeInfo[parentID]:   # for each succesor
343            f = QTreeWidgetItem(parentLVI);
344            f.term = self.mesh.toName[i]
345            data = [self.mesh.toName[i]]
346            if soloMode:
347                rfr = str(self.results[i])
348                data.append(rfr)
349            else:          # when we have referece and cluster dataset we have to print additional info
350                rfr = str(self.results[i][0])
351                cfr = str(self.results[i][1])
352                pval = "%.4g" % self.results[i][2]
353                fold = "%.4g" % self.results[i][3]
354                data.extend([rfr, cfr, pval, fold])
355            self.lvItem2Mesh[f] = (data[0], i)      # mapping   QTreeWidgetItem <-> mesh id
356            for t in range(len(data)):
357                f.setText(t, data[t])
358            self.__treeViewMaker__(f, i, soloMode)
359        return True
360
361    def refresh(self):
362        """
363        Function refresh is executed as number of widget inputs changes.
364        """
365        if self.reference or self.cluster:
366            self.__switchGUI__()
367            self.__updateData__()
368
369    def __clearGUI__(self):
370        """
371        Function __clearGUI__ sets tree view, table and labels to their default values.
372        """
373        self.meshLV.clear()
374        self.sigTermsTable.setRowCount(0)
375
376    def __switchGUI__(self):
377        """
378        Function __switchGUI__ is capable of changing GUI based on number of connected inputs.
379        """
380        if(len(self.cluster) > len(self.reference)):
381            self.optionsBox.setDisabled(1)
382            #self.warning("Cluster dataset is greater than reference dataset. Please check the widget inputs.")
383            QMessageBox.warning(None, "Invalid input dataset length", "Cluster dataset is longer than the reference dataset. Please check the widget inputs.", QMessageBox.Ok)
384            return False
385        if not self.reference and not self.cluster:
386            self.optionsBox.setDisabled(1)
387            return
388        self.optionsBox.setDisabled(0)
389        solo = True
390        if self.reference and self.cluster:
391            solo = False
392        if solo:
393            self.maxp.setDisabled(1)
394            self.minf.setDisabled(0)
395            for i in range(2, len(self.columns)):
396                self.meshLV.hideColumn(i)
397                self.sigTermsTable.hideColumn(i)
398
399            self.sigTermsTable.setHorizontalHeaderLabels(["MeSH term", "frequency"])
400            self.meshLV.setHeaderLabels(["MeSH term", "frequency"])
401            self.ratio.setText("")
402        else:
403            self.maxp.setDisabled(0)
404            self.minf.setDisabled(1)
405            for i in range(0, len(self.columns)):
406                self.meshLV.showColumn(i)
407                self.sigTermsTable.showColumn(i)
408            self.sigTermsTable.setHorizontalHeaderLabels(self.columns)
409            self.meshLV.setHeaderLabels(self.columns)
410            for i in range(0, len(self.columns)):
411                self.meshLV.header().resizeSection(i, self.col_size[i])
412                self.sigTermsTable.horizontalHeader().resizeSection(i, self.col_size[i])
413            self.ratio.setText("ratio = %.4g" % self.mesh.ratio)
414
415    def getReferenceData(self, data):
416        """
417        Function getReferenceData is executed once reference signal is connected to the widget.
418        """
419        if data:
420            self.reference = data
421            self.infoa.setText('%d reference examples' % len(data))
422        else:
423            self.reference = None
424            self.infoa.setText('No reference data.')
425            self.ref_att.setText('')
426
427        if self.reference or self.cluster:
428            self.__switchGUI__()
429            self.__updateData__()
430        else:
431            self.__clearGUI__()
432
433    def getClusterData(self, data):
434        """
435        Function getClusterData is executed once cluster signal is connected to the widget.
436        """
437        if data:
438            self.cluster = data
439            self.infob.setText('%d cluster examples' % len(data))
440        else:
441            self.cluster = None
442            self.infob.setText('No cluster data.')
443            self.clu_att.setText('')
444
445        if self.reference or self.cluster:
446            self.__switchGUI__()
447            self.__updateData__()
448        else:
449            self.__clearGUI__()
Note: See TracBrowser for help on using the repository browser.