source: orange-bioinformatics/_bioinformatics/widgets/OWMeSHBrowser.py @ 1726:6778e0225b86

Revision 1726:6778e0225b86, 14.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 17 months ago (diff)

Added new icons by Peter Cuhalev and replaced existing ones with expanded paths.

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