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

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

Added new style widget meta descriptions.

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