source: orange-bioinformatics/widgets/OWMeSHBrowser.py @ 1462:ee4cee02fb85

Revision 1462:ee4cee02fb85, 14.4 KB checked in by markotoplak, 3 years ago (diff)

Bioinformatics widgets name changes, new icon for GeneMANIA.

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