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

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

Added new style widget meta descriptions.

RevLine 
[188]1"""
[764]2<name>Data Profiles</name>
[1051]3<description>Visualization of data profiles (e.g., time series).</description>
[188]4<contact>Tomaz Curk</contact>
[1726]5<icon>icons/ExpressionProfiles.svg</icon>
[1052]6<priority>1030</priority>
[188]7"""
8
[1643]9from Orange.OrangeWidgets import OWGUI
[1632]10from Orange.OrangeWidgets.OWGraph import *
11from Orange.OrangeWidgets.OWToolbars import ZoomSelectToolbar
12from Orange.OrangeWidgets.OWTools import *
13from Orange.OrangeWidgets.OWWidget import *
[188]14import statc
15
[1874]16NAME = "Data Profiles"
17DESCRIPTION = "Visualization of data profiles (e.g., time series)."
18ICON = "icons/ExpressionProfiles.svg"
19PRIORITY = 1030
20
21INPUTS = [("Examples", Orange.data.Table, "data", Multiple + Default)]
22OUTPUTS = [("Examples", Orange.data.Table)]
23
24REPLACES = ["_bioinformatics.widgets.OWDisplayProfiles.OWDisplayProfiles"]
25
[188]26## format of data:
27##     largestAdjacentValue
28##     yq3
29##     yavg
30##     yqM
31##     yq1
32##     smallestAdjacentValue
33
34class boxPlotQwtPlotCurve(QwtPlotCurve):
[415]35    def __init__(self, text = None, connectPoints = 1, tickXw = 1.0/5.0):
36        QwtPlotCurve.__init__(self, QwtText(text))
[188]37        self.connectPoints = connectPoints
38        self.tickXw = tickXw
39        self.boxPlotPenWidth = 2
40
[419]41    def draw(self, p, xMap, yMap, f=0, t=-1):
[188]42        # save ex settings
[415]43##        pen = p.pen()
44##        brush = p.brush()
45        p.save()
46        if type(f)==QRect:
[419]47            f = 0 
[188]48       
49        p.setBackgroundMode(Qt.OpaqueMode)
50        p.setPen(self.pen())
[415]51        if self.style() == QwtPlotCurve.UserCurve:
[188]52            back = p.backgroundMode()
53            p.setBackgroundMode(Qt.OpaqueMode)
54            if t < 0: t = self.dataSize() - 1
55
56            if f % 6 <> 0: f -= f % 6
57            if t % 6 <> 0:  t += 6 - (t % 6)
58            ## first connect averages
59            first = 1
60            if self.connectPoints: 
61                for i in range(f, t, 6):
62#                    py = yqM = yMap.transform(self.y(i + 3))
63                    py = yavg = yMap.transform(self.y(i + 2))
64                    px = xMap.transform(self.x(i))
65                    if first:
66                        first = 0
67                    else:
68                        p.drawLine(ppx, ppy, px, py)
69                    ppx = px
70                    ppy = py
71
72            ## then draw boxes
73            if self.boxPlotPenWidth > 0:
74                np = QPen(self.pen())
75                np.setWidth(self.boxPlotPenWidth)
76                p.setPen(np)
77                for i in range(f, t, 6):
78                    largestAdjVal = yMap.transform(self.y(i))
79                    yq3 = yMap.transform(self.y(i + 1))
80                    yavg = yMap.transform(self.y(i + 2))
81                    yqM = yMap.transform(self.y(i + 3))
82                    yq1 = yMap.transform(self.y(i + 4))
83                    smallestAdjVal = yMap.transform(self.y(i + 5))
84
85                    px = xMap.transform(self.x(i))
86                    wxl = xMap.transform(self.x(i) - self.tickXw/2.0)
87                    wxr = xMap.transform(self.x(i) + self.tickXw/2.0)
88
89                    p.drawLine(wxl, largestAdjVal, wxr,   largestAdjVal) ## - upper whisker
90                    p.drawLine(px, largestAdjVal, px, yq3)               ## | connection between upper whisker and q3
91                    p.drawRect(wxl, yq3, wxr - wxl, yq1 - yq3)           ## box from q3 to q1
92                    p.drawLine(wxl, yqM, wxr, yqM)                       ## median line
93                    p.drawLine(px, yq1, px, smallestAdjVal)              ## | connection between q1 and lower whisker
94                    p.drawLine(wxl, smallestAdjVal, wxr, smallestAdjVal) ## _ lower whisker
95
96                    ## average line (circle)
97                    p.drawEllipse(px - 3, yavg - 3, 6, 6)
98
99            p.setBackgroundMode(back)
100        else:
101            QwtPlotCurve.draw(self, p, xMap, yMap, f, t)
102
103        # restore ex settings
[415]104        p.restore()
105##        p.setPen(pen)
106##        p.setBrush(brush)
[188]107
108    def setBoxPlotPenWidth(self, v):
109        self.boxPlotPenWidth = v
[415]110        self.itemChanged()
[188]111
112class profilesGraph(OWGraph):
[840]113    def __init__(self, master, parent = None, name = None, title = ""):
[188]114        OWGraph.__init__(self, parent, name)
[840]115        self.master = master
[188]116        self.setYRlabels(None)
117        self.enableGridXB(0)
118        self.enableGridYL(0)
119        self.setAxisMaxMajor(QwtPlot.xBottom, 10)
120        self.setAxisMaxMinor(QwtPlot.xBottom, 0)
121        self.setAxisMaxMajor(QwtPlot.yLeft, 10)
122        self.setAxisMaxMinor(QwtPlot.yLeft, 5)
123        self.setShowMainTitle(1)
124        self.setMainTitle(title)
125        self.setAxisAutoScale(QwtPlot.xBottom)
126        self.setAxisAutoScale(QwtPlot.xTop)
127        self.setAxisAutoScale(QwtPlot.yLeft)
128        self.setAxisAutoScale(QwtPlot.yRight)
129
130        self.showAverageProfile = 1
131        self.showSingleProfiles = 0
[965]132        self.curveWidth = 1
133        self.renderAntialiased = True
[188]134##        self.groups = [('grp1', ['0', '2', '4']), ('grp2', ['4', '6', '8', '10', '12', '14']), ('grp3', ['16', '18'])]
135
[965]136        self.selectedCurves = []
137        self.highlightedCurve = None
[188]138        self.removeCurves()
139##        self.connect(self, SIGNAL("plotMouseMoved(const QMouseEvent &)"), self.onMouseMoved)
140
141    def removeCurves(self):
[415]142##        OWGraph.removeCurves(self)
143        self.clear()
[188]144        self.classColor = None
145        self.classBrighterColor = None
146        self.profileCurveKeys = []
147        self.averageProfileCurveKeys = []
148        self.showClasses = []
149
150    def setData(self, data, classColor, classBrighterColor, ShowAverageProfile, ShowSingleProfiles, progressBar = None):
151        self.removeCurves()
152        self.classColor = classColor
153        self.classBrighterColor = classBrighterColor
154        self.showAverageProfile = ShowAverageProfile
155        self.showSingleProfiles = ShowSingleProfiles
156
157        self.groups = [('grp', data.domain.attributes)]
158        ## remove any non continuous attributes from list
159        ## at the same time convert any attr. string name into orange var type
160        filteredGroups = []
161        for (grpname, grpattrs) in self.groups:
162            filteredGrpAttrs = []
163            for a in grpattrs:
164                var = data.domain[a]
165                if (var.varType == orange.VarTypes.Continuous):
166                    filteredGrpAttrs.append(var)
167                else:
168                    print "warning, skipping attribute:", a
169            if len(filteredGrpAttrs) > 0:
170                filteredGroups.append( (grpname, filteredGrpAttrs) )
171        self.groups = filteredGroups
172       
173        ## go group by group
174        avgCurveData = []
175        boxPlotCurveData = []
176        ccn = 0
[840]177        if data.domain.classVar and data.domain.classVar.varType <> orange.VarTypes.Discrete:
[188]178            print "error, class variable not discrete:", data.domain.classVar
179            return
[840]180        classes = data.domain.classVar.values if data.domain.classVar else ["(No class)"]
181        allc = len(data.domain.classVar.values) if data.domain.classVar else 1
182##        print data, len(data)
183        for i, c in enumerate(classes):
184            if progressBar <> None: progressBar(i*100.0/len(classes))
185            classSymb = QwtSymbol(QwtSymbol.Ellipse, QBrush(self.classBrighterColor[i]), QPen(self.classBrighterColor[i]), QSize(7,7)) ##self.black
[188]186            self.showClasses.append(0)
187
188            self.profileCurveKeys.append([])
189            self.averageProfileCurveKeys.append([])
190            allg = len(self.groups)
191            gcn = 0
192            grpcnx = 0
193            for (grpname, grpattrs) in self.groups:
[840]194                oneClassData = data.select({data.domain.classVar.name:c}) if data.domain.classVar else data
[188]195                oneGrpData = oneClassData.select(orange.Domain(grpattrs, oneClassData.domain))
196
197                ## single profiles
198                nativeData = oneGrpData.native(2)
199                yVals = [[] for cn in range(len(grpattrs))]
200                alle = len(nativeData)
201                ecn = 0
[965]202                for e, example in zip(nativeData, oneClassData):
[188]203                    ecn += 1
204                    progress = 100.0*(ccn + (float(gcn)/allg) * (float(ecn)/alle))
205                    progress = int(round(progress / allc))
206                    if progressBar <> None: progressBar(progress)
207                    y = []
208                    x = []
209                    xcn = grpcnx
210                    vcn = 0
211                    en = e.native(1)
212                    for v in en:
213                        if not v.isSpecial():
214                            yVal = v.native()
215                            yVals[vcn].append( yVal )
216                            y.append( yVal )
217                            x.append( xcn )
218                        xcn += 1
219                        vcn += 1
[415]220                    curve = self.addCurve('', style = QwtPlotCurve.Lines)
[965]221                    curve.example = example
[188]222                    if self.showAverageProfile:
[415]223                        curve.setPen(QPen(self.classColor[ccn], 1))
[188]224                    else:
[415]225                        curve.setPen(QPen(self.classBrighterColor[ccn], 1))
226                    curve.setData(x, y)
227                    curve.setSymbol(classSymb)
228                    self.profileCurveKeys[-1].append(curve)
[188]229
230                ## average profile and box plot
231                BPx = []
232                BPy = []
233                xcn = grpcnx
234                vcn = 0
235                dist = orange.DomainDistributions(oneGrpData)
236                for a in dist:
237                    if a and len(a) > 0:
238                        ## box plot data
239                        yavg = a.average()
240                        yq1 = a.percentile(25)
241                        yqM = a.percentile(50)
242                        yq3 = a.percentile(75)
243
244                        iqr = yq3 - yq1
245                        yLowerCutOff = yq1 - 1.5 * iqr
246                        yUpperCutOff = yq3 + 1.5 * iqr
247                       
248                        yVals[vcn].sort() 
249                        ## find the smallest value above the lower inner fence
250                        smallestAdjacentValue = None
251                        for v in yVals[vcn]:
252                            if v >= yLowerCutOff:
253                                smallestAdjacentValue = v
254                                break
255
256                        yVals[vcn].reverse()
257                        ## find the largest value below the upper inner fence
258                        largestAdjacentValue = None
259                        for v in yVals[vcn]:
260                            if v <= yUpperCutOff:
261                                largestAdjacentValue = v
262                                break
263                        BPy.append( largestAdjacentValue )
264                        BPy.append( yq3 )
265                        BPy.append( yavg )
266                        BPy.append( yqM )
267                        BPy.append( yq1 )
268                        BPy.append( smallestAdjacentValue )
269                        BPx.append( xcn )
270                        BPx.append( xcn )
271                        BPx.append( xcn )
272                        BPx.append( xcn )
273                        BPx.append( xcn )
274                        BPx.append( xcn )
275
276                    xcn += 1
277                    vcn += 1
278
279                boxPlotCurveData.append( (BPx, BPy, ccn) )
280                grpcnx += len(grpattrs)
281                gcn +=1
282            ccn += 1
283
284        for (x, y, tmpCcn) in boxPlotCurveData:
285            classSymb = QwtSymbol(QwtSymbol.Cross, QBrush(self.classBrighterColor[tmpCcn]), QPen(self.classBrighterColor[tmpCcn]), QSize(8,8))
[415]286            curve = boxPlotQwtPlotCurve('', connectPoints = 1, tickXw = 1.0/5.0)
287            curve.attach(self)
288            curve.setPen(QPen(self.classBrighterColor[tmpCcn], 3))
289            curve.setStyle(QwtPlotCurve.UserCurve)
290            curve.setSymbol(classSymb)
291            curve.setData(x, y)
292            self.averageProfileCurveKeys[tmpCcn].append(curve)
[188]293
294        ## generate labels for attributes
295        labels = []
296        for (grpname, grpattrs) in self.groups:
297            for a in grpattrs:
298                labels.append( a.name)
299
[415]300        if None: 
301            self.setXlabels(labels)
[188]302        self.updateCurveDisplay()
303
304    def updateCurveDisplay(self):
[840]305        profilesCount = 0
306        for cNum in range(len(self.showClasses or [1])):
307            showCNum = (self.showClasses[cNum] <> 0) if len(self.showClasses) > 1 else True
308##            print cNum, showCNum
[188]309            ## single profiles
310            bSingle = showCNum and self.showSingleProfiles
311            bAve = showCNum and self.showAverageProfile ## 1 = show average profiles for now
312            for ckey in self.profileCurveKeys[cNum]:
[415]313                curve =  ckey #self.curve(ckey)
[188]314                if curve <> None:
[415]315                    curve.setVisible(bSingle)
[840]316                    profilesCount += 1 if bSingle else 0
[415]317##                    qp = self.curvePen(ckey)
318                    qp = curve.pen()
[188]319                    if not(bAve):
320                        qp.setColor(self.classBrighterColor[cNum])
321                    else:
322                        qp.setColor(self.classColor[cNum])
[415]323##                    curve.setPen(qp)
[188]324
325            ## average profiles
326            for ckey in self.averageProfileCurveKeys[cNum]:
[415]327                curve =  ckey #self.curve(ckey)
328                if curve <> None: curve.setVisible(bAve)
[188]329
[415]330##        self.updateLayout()
[840]331        self.master.infoLabel.setText("Showing %i profiles" % profilesCount)
[965]332        self.setCurveRenderHints()
[415]333        self.replot()
[188]334
335    def setShowClasses(self, list):
336        self.showClasses = list
337        self.updateCurveDisplay()
338
339    def setShowAverageProfile(self, v):
340        self.showAverageProfile = v
341        self.updateCurveDisplay()
342
343    def setShowSingleProfiles(self, v):
344        self.showSingleProfiles = v
345        self.updateCurveDisplay()
346
347    def setPointWidth(self, v):
348        for cNum in range(len(self.showClasses)):
[415]349            for curve in self.profileCurveKeys[cNum]:
350                symb = curve.symbol()
[188]351                symb.setSize(v, v)
[415]352##                curve.setSymbol(symb)
353        self.replot()
[188]354
355    def setCurveWidth(self, v):
[965]356        fix = dict.fromkeys(self.selectedCurves, 2)
357        fix[self.highlightedCurve] = 3
[188]358        for cNum in range(len(self.showClasses)):
[415]359            for curve in self.profileCurveKeys[cNum]:
360                qp = curve.pen()
[965]361                qp.setWidth(v + fix.get(curve, 0))
[415]362##                curve.setPen(qp)
[965]363        self.curveWidth = v
[415]364        self.replot()
[188]365
366    def setAverageCurveWidth(self, v):
367        for cNum in range(len(self.showClasses)):
[415]368            for curve in self.averageProfileCurveKeys[cNum]:
369                qp = curve.pen()
[188]370                qp.setWidth(v)
[415]371##                curve.setPen(qp)
372        self.replot()
[188]373
374    def setBoxPlotWidth(self, v):
375        for cNum in range(len(self.showClasses)):
[415]376            for curve in self.averageProfileCurveKeys[cNum]:
377##                c = self.curve(ckey)
378                curve.setBoxPlotPenWidth(v)
379        self.replot()
[188]380
381    def sizeHint(self):
382        return QSize(170, 170)
383
[965]384    def closestCurve(self, point):
385        pointDistances = [(curve,) + curve.closestPoint(point) for curve in self.itemList() if isinstance(curve, QwtPlotCurve) and curve.isVisible()]
[1175]386        return min(pointDistances, key=lambda t:t[-1]) if pointDistances else (None, -1, sys.maxint)
[965]387
388    def curveUnderMouse(self, pos):
389        pos = self.canvas().mapFrom(self, pos)
390        curve, i, d = self.closestCurve(pos)
391        if d <= 5 and hasattr(curve, "example"):
392            return curve
393        else:
394            return None
395   
396    def mouseMoveEvent(self, event):
397        curve = self.curveUnderMouse(event.pos())
398        if self.highlightedCurve != curve:
399            if self.highlightedCurve:
400                self.highlightedCurve.setPen(QPen(self.highlightedCurve.pen().color(), self.curveWidth + (2 if self.highlightedCurve in self.selectedCurves else 0)))
401            if curve:
402                curve.setPen(QPen(curve.pen().color(), self.curveWidth + 3))
403            self.highlightedCurve = curve
404            self.replot()
405           
406            if curve:
407                QToolTip.showText(event.globalPos(), "")
408                QToolTip.showText(event.globalPos(), str(curve.example[self.master.profileLabel]))
409                                         
410        return OWGraph.mouseMoveEvent(self, event)
411   
412    def mousePressEvent(self, event):
413        curve = self.curveUnderMouse(event.pos())
414        if curve and event.button() == Qt.LeftButton and self.state == SELECT:
415            if self.master.ctrlPressed:
416                if curve in self.selectedCurves:
417                    self.setSelectedCurves([c for c in self.selectedCurves if c is not curve])
418                else:
419                    self.setSelectedCurves(self.selectedCurves + [curve])
420            else:
421                self.setSelectedCurves([curve])
422            self.replot()
423               
424        return OWGraph.mousePressEvent(self, event)
425   
426    def setCurveRenderHints(self):
427        for item in self.itemList():
428            if isinstance(item, QwtPlotCurve):
429                item.setRenderHint(QwtPlotCurve.RenderAntialiased, self.renderAntialiased)
430        self.replot()
431       
432    def setSelectedCurves(self, curves=[]):
433        for c in self.selectedCurves:
434            c.setPen(QPen(c.pen().color(), self.curveWidth))
435        for c in curves:
436            c.setPen(QPen(c.pen().color(), self.curveWidth + 2))
437        self.selectedCurves = curves
438        self.master.commitIf()
439       
440    def removeLastSelection(self):
441        self.setSelectedCurves(self.selectedCurves[:-1])
442        self.replot()
443       
444    def removeAllSelections(self):
445        self.setSelectedCurves([])
446        self.replot()
447       
448    def sendData(self):
449        self.master.commitIf()
[188]450
451
452class OWDisplayProfiles(OWWidget):
[965]453    settingsList = ["SelectedClasses", "PointWidth", "CurveWidth", "AverageCurveWidth", "BoxPlotWidth", "ShowAverageProfile", "ShowSingleProfiles", "CutEnabled", "CutLow", "CutHigh", "autoSendSelected"]
[841]454    contextHandlers = {"": DomainContextHandler("", [ContextField("SelectedClasses")], loadImperfect=False)}
[188]455    def __init__(self, parent=None, signalManager = None):
[965]456#        self.callbackDeposit = [] # deposit for OWGUI callback functions
[188]457        OWWidget.__init__(self, parent, signalManager, 'Expression Profiles', 1)
458
459        #set default settings
460        self.ShowAverageProfile = 1
461        self.ShowSingleProfiles = 0
462        self.PointWidth = 2
463        self.CurveWidth = 1
464        self.AverageCurveWidth = 4
465        self.BoxPlotWidth = 2
[415]466        self.SelectedClasses = []
[841]467        self.Classes = []
[965]468        self.autoSendSelected = 0
469        self.selectionChangedFlag = False
[188]470
471        self.CutLow = 0; self.CutHigh = 0; self.CutEnabled = 0
[965]472       
473        self.profileLabel = None
[188]474
475        #load settings
476        self.loadSettings()
477
478        # GUI
[840]479        self.graph = profilesGraph(self, self.mainArea, "")
[415]480        self.mainArea.layout().addWidget(self.graph)
[188]481        self.graph.hide()
482        self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
483
484        # GUI definition
[415]485        self.tabs = OWGUI.tabWidget(self.space)
[188]486
487        # GRAPH TAB
[415]488        GraphTab = OWGUI.createTabPage(self.tabs, "Graph")
[188]489
490        ## display options
[840]491        self.infoLabel = OWGUI.widgetLabel(OWGUI.widgetBox(GraphTab, "Info"), "No data on input.")
[415]492        displayOptBox = OWGUI.widgetBox(GraphTab, "Display") #QVButtonGroup("Display", GraphTab)
[188]493        displayOptButtons = ['Majority Class', 'Majority Class Probability', 'Target Class Probability', 'Number of Instances']
494        OWGUI.checkBox(displayOptBox, self, 'ShowSingleProfiles', 'Expression Profiles', tooltip='', callback=self.updateShowSingleProfiles)
495        OWGUI.checkBox(displayOptBox, self, 'ShowAverageProfile', 'Box Plot', tooltip='', callback=self.updateShowAverageProfile)
496
497        ## class selection (classQLB)
[415]498        self.classQVGB = OWGUI.widgetBox(GraphTab, "Classes") #QVGroupBox(GraphTab)
499##        self.classQVGB.setTitle("Classes")
[841]500        self.classQLB = OWGUI.listBox(self.classQVGB, self, "SelectedClasses", "Classes", selectionMode=QListWidget.MultiSelection, callback=self.classSelectionChange) #QListBox(self.classQVGB)
[415]501##        self.classQLB.setSelectionMode(QListBox.Multi)
[841]502        self.unselectAllClassedQLB = OWGUI.button(self.classQVGB, self, "Unselect all", callback=self.SUAclassQLB) #QPushButton("(Un)Select All", self.classQVGB)
[415]503##        self.connect(self.unselectAllClassedQLB, SIGNAL("clicked()"), self.SUAclassQLB)
504##        self.connect(self.classQLB, SIGNAL("selectionChanged()"), self.classSelectionChange)
[188]505
506        ## show single/average profile
507##        self.showAverageQLB = QPushButton("Box Plot", self.classQVGB)
508##        self.showAverageQLB.setToggleButton(1)
509##        self.showAverageQLB.setOn(self.ShowAverageProfile)
510##        self.showSingleQLB = QPushButton("Single Profiles", self.classQVGB)
511##        self.showSingleQLB.setToggleButton(1)
512##        self.showSingleQLB.setOn(self.ShowSingleProfiles)
513##        self.connect(self.showAverageQLB, SIGNAL("toggled(bool)"), self.updateShowAverageProfile)
514##        self.connect(self.showSingleQLB, SIGNAL("toggled(bool)"), self.updateShowSingleProfiles)
515
[415]516##        self.tabs.insertTab(GraphTab, "Graph")
[188]517
518        # SETTINGS TAB
[415]519        SettingsTab = OWGUI.createTabPage(self.tabs, "Settings") #QVGroupBox(self)
[188]520
[965]521        self.profileLabelComboBox = OWGUI.comboBox(SettingsTab, self, 'profileLabel', 'Profile Labels', sendSelectedValue=True, valueType=str)
[188]522        OWGUI.hSlider(SettingsTab, self, 'PointWidth', box='Point Width', minValue=0, maxValue=9, step=1, callback=self.updatePointWidth, ticks=1)
523        OWGUI.hSlider(SettingsTab, self, 'CurveWidth', box='Profile Width', minValue=1, maxValue=9, step=1, callback=self.updateCurveWidth, ticks=1)
524        OWGUI.hSlider(SettingsTab, self, 'AverageCurveWidth', box='Average Profile Width', minValue=1, maxValue=9, step=1, callback=self.updateAverageCurveWidth, ticks=1)
525        OWGUI.hSlider(SettingsTab, self, 'BoxPlotWidth', box='Box Plot Width', minValue=0, maxValue=9, step=1, callback=self.updateBoxPlotWidth, ticks=1)
[965]526        OWGUI.checkBox(SettingsTab, self, 'graph.renderAntialiased', "Render antialiased", callback=self.graph.setCurveRenderHints)
[188]527
528
[415]529    ## graph y scale
530        box = OWGUI.widgetBox(SettingsTab, "Threshold/ Values") #QVButtonGroup("Threshol/ Values", SettingsTab)
[188]531        OWGUI.checkBox(box, self, 'CutEnabled', "Enabled", callback=self.setCutEnabled)
532        self.sliderCutLow = OWGUI.qwtHSlider(box, self, 'CutLow', label='Low:', labelWidth=33, minValue=-20, maxValue=0, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.updateYaxis)
533        self.sliderCutHigh = OWGUI.qwtHSlider(box, self, 'CutHigh', label='High:', labelWidth=33, minValue=0, maxValue=20, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.updateYaxis)
534        if not self.CutEnabled:
535            self.sliderCutLow.box.setDisabled(1)
536            self.sliderCutHigh.box.setDisabled(1)
537
[415]538##        self.tabs.insertTab(SettingsTab, "Settings")
[965]539
540        self.toolbarSelection = ZoomSelectToolbar(self, self.controlArea, self.graph, self.autoSendSelected, buttons=(ZoomSelectToolbar.IconZoom, ZoomSelectToolbar.IconPan, ZoomSelectToolbar.IconSelect, ZoomSelectToolbar.IconSpace,
541                                                                       ZoomSelectToolbar.IconRemoveLast, ZoomSelectToolbar.IconRemoveAll, ZoomSelectToolbar.IconSendSelection))
542        cb = OWGUI.checkBox(self.controlArea, self, "autoSendSelected", "Auto send on selection change")
543        OWGUI.setStopper(self, self.toolbarSelection.buttonSendSelections, cb, "selectionChangedFlag", self.commit)
[188]544       
545        # inputs
546        # data and graph temp variables
547       
548        self.inputs = [("Examples", ExampleTable, self.data, Default + Multiple)]
549        self.outputs = [("Examples", ExampleTable, Default)]
550
551        # temp variables
552        self.MAdata = []
553        self.classColor = None
554        self.classBrighterColor = None
555        self.numberOfClasses  = 0
556        self.classValues = []
[965]557        self.ctrlPressed = False
558        self.attrIcons = self.createAttributeIconDict()
[188]559
560        self.graph.canvas().setMouseTracking(1)
561
[965]562#        self.zoomStack = []
[415]563##        self.connect(self.graph,
564##                     SIGNAL('plotMousePressed(const QMouseEvent&)'),
565##                     self.onMousePressed)
566##        self.connect(self.graph,
567##                     SIGNAL('plotMouseReleased(const QMouseEvent&)'),
568##                     self.onMouseReleased)
[188]569        self.resize(800,600)
570
571    def updateYaxis(self):
572        if (not self.CutEnabled):
573            self.graph.setAxisAutoScale(QwtPlot.yLeft)
574            self.graph.replot()
575            return
576                                                                                                                                     
577        self.graph.setAxisScale(QwtPlot.yLeft, self.CutLow, self.CutHigh)
578        self.graph.replot()
579
580    def setCutEnabled(self):
581        self.sliderCutLow.box.setDisabled(not self.CutEnabled)
582        self.sliderCutHigh.box.setDisabled(not self.CutEnabled)
583        self.updateYaxis()
584
[965]585#    def onMousePressed(self, e):
586#        if Qt.LeftButton == e.button():
587#            # Python semantics: self.pos = e.pos() does not work; force a copy
588#            self.xpos = e.pos().x()
589#            self.ypos = e.pos().y()
590#            self.graph.enableOutline(1)
591#            self.graph.setOutlinePen(QPen(Qt.black))
592#            self.graph.setOutlineStyle(Qwt.Rect)
593#            self.zooming = 1
594#            if self.zoomStack == []:
595#                self.zoomState = (
596#                    self.graph.axisScale(QwtPlot.xBottom).lBound(),
597#                    self.graph.axisScale(QwtPlot.xBottom).hBound(),
598#                    self.graph.axisScale(QwtPlot.yLeft).lBound(),
599#                    self.graph.axisScale(QwtPlot.yLeft).hBound(),
600#                    )
601#        elif Qt.RightButton == e.button():
602#            self.zooming = 0
603#        # fake a mouse move to show the cursor position
604#
605#    # onMousePressed()
606#
607#    def onMouseReleased(self, e):
608#        if Qt.LeftButton == e.button():
609#            xmin = min(self.xpos, e.pos().x())
610#            xmax = max(self.xpos, e.pos().x())
611#            ymin = min(self.ypos, e.pos().y())
612#            ymax = max(self.ypos, e.pos().y())
613#            self.graph.setOutlineStyle(Qwt.Cross)
614#            xmin = self.graph.invTransform(QwtPlot.xBottom, xmin)
615#            xmax = self.graph.invTransform(QwtPlot.xBottom, xmax)
616#            ymin = self.graph.invTransform(QwtPlot.yLeft, ymin)
617#            ymax = self.graph.invTransform(QwtPlot.yLeft, ymax)
618#            if xmin == xmax or ymin == ymax:
619#                return
620#            self.zoomStack.append(self.zoomState)
621#            self.zoomState = (xmin, xmax, ymin, ymax)
622#            self.graph.enableOutline(0)
623#        elif Qt.RightButton == e.button():
624#            if len(self.zoomStack):
625#                xmin, xmax, ymin, ymax = self.zoomStack.pop()
626#            else:
627#                self.graph.setAxisAutoScale(QwtPlot.xBottom)
628#                self.graph.setAxisAutoScale(QwtPlot.yLeft)
629#                self.graph.replot()
630#                return
631#
632#        self.graph.setAxisScale(QwtPlot.xBottom, xmin, xmax)
633#        self.graph.setAxisScale(QwtPlot.yLeft, ymin, ymax)
634#        self.graph.replot()
635#
636#    def saveToFile(self):
637#        qfileName = QFileDialog.getSaveFileName("graph.png","Portable Network Graphics (.PNG)\nWindows Bitmap (.BMP)\nGraphics Interchange Format (.GIF)", None, "Save to..")
638#        fileName = str(qfileName)
639#        if fileName == "": return
640#        (fil,ext) = os.path.splitext(fileName)
641#        ext = ext.replace(".","")
642#        ext = ext.upper()
643#        cl = 0
644#        for g in self.graphs:
645#            if g.isVisible():
646#                clfname = fil + "_" + str(cl) + "." + ext
647#                g.saveToFileDirect(clfname, ext)
648#            cl += 1
[188]649
650    def updateShowAverageProfile(self):
651        self.graph.setShowAverageProfile(self.ShowAverageProfile)
652
653    def updateShowSingleProfiles(self):
654        self.graph.setShowSingleProfiles(self.ShowSingleProfiles)
655
656    def updatePointWidth(self):
657        self.graph.setPointWidth(self.PointWidth)
658
659    def updateCurveWidth(self):
660        self.graph.setCurveWidth(self.CurveWidth)
661
662    def updateAverageCurveWidth(self):
663        self.graph.setAverageCurveWidth(self.AverageCurveWidth)
664
665    def updateBoxPlotWidth(self):
666        self.graph.setBoxPlotWidth(self.BoxPlotWidth)
667       
668    ##
669    def selectUnselectAll(self, qlb):
[841]670        self.SelectedClasses = range(len(self.Classes)) if len(self.SelectedClasses) != len(self.Classes)  else []
671       
672        self.classSelectionChange()
[188]673
674    def SUAclassQLB(self):
675        self.selectUnselectAll(self.classQLB)
676    ##
677
678    ## class selection (classQLB)
679    def classSelectionChange(self):
[415]680##        list = []
681##        selCls = []
682##        for i in range(self.classQLB.count()):
683##            if self.classQLB.isSelected(i):
684##                list.append( 1 )
685##                selCls.append(self.classValues[i])
686##            else:
687##                list.append( 0 )
688        list = [1 if i in self.SelectedClasses else 0 for i in range(self.classQLB.count())]
[841]689        self.unselectAllClassedQLB.setText("Select all" if len(self.SelectedClasses) != len(self.Classes) else "Unselect all")
[415]690        selCls = [self.classValues[i] for i in self.SelectedClasses]
[188]691        self.graph.setShowClasses(list)
[965]692#        if selCls == []:
693#            self.send("Examples", None)
694#        else:
695#            if len(self.MAdata) > 1:
696#                newet = orange.ExampleTable(self.MAdata[0].domain)
697#                for idx, i in enumerate(list):
698#                    if i: newet.extend(self.MAdata[idx])
699#            elif self.MAdata[0].domain.classVar:
700#                newet = self.MAdata[0].filter({self.MAdata[0].domain.classVar.name:selCls})
701#            else:
702#                newet = self.MAdata[0]
703#            self.send("Examples", newet)
[188]704    ##
705
706    def calcGraph(self):
707        ## compute from self.MAdata
708        if len(self.MAdata) == 1:
709            d = self.MAdata[0]
710        elif len(self.MAdata) > 0:
711            combCvals = [str(nd.name) for nd in self.MAdata]
712            combClass = orange.EnumVariable('file', values=combCvals)
713            combDomain = orange.Domain(self.MAdata[0].domain.attributes + [combClass])
714            d = orange.ExampleTable(combDomain)
715           
716            for tmpd in self.MAdata:
717                newtmpd = tmpd.select(combDomain)
718                for te in newtmpd:
719                    te.setclass(tmpd.name)
720                d.extend(newtmpd)
721        else:
722            return
723
724        self.progressBarInit()
725        self.graph.setData(d, self.classColor, self.classBrighterColor, self.ShowAverageProfile, self.ShowSingleProfiles, self.progressBarSet)
726        self.graph.setPointWidth(self.PointWidth)
727        self.graph.setCurveWidth(self.CurveWidth)
728        self.graph.setAverageCurveWidth(self.AverageCurveWidth)
[415]729        self.graph.setBoxPlotWidth(self.BoxPlotWidth)
[188]730
731        self.graph.setAxisAutoScale(QwtPlot.xBottom)
732##        self.graph.setAxisAutoScale(QwtPlot.yLeft)
733        self.updateYaxis()
734        self.progressBarFinished()
735
736    def newdata(self):
737        self.classQLB.clear()
[965]738        self.profileLabelComboBox.clear()
[840]739##        if len(self.MAdata) > 1 or (len(self.MAdata) == 1 and self.MAdata[0].domain.classVar.varType == orange.VarTypes.Discrete):
740        if len(self.MAdata) >= 1:
[188]741            ## classQLB
[840]742            if len(self.MAdata) == 1 and self.MAdata[0].domain.classVar and self.MAdata[0].domain.classVar.varType == orange.VarTypes.Discrete:
[188]743                self.numberOfClasses = len(self.MAdata[0].domain.classVar.values)
744            else:
745                self.numberOfClasses = len(self.MAdata)
746            self.classColor = ColorPaletteGenerator(self.numberOfClasses)
747            self.classBrighterColor = [self.classColor[c, 160] for c in range(self.numberOfClasses)]
748
749            self.calcGraph()
750            ## update graphics
751            ## classQLB
752            self.classQVGB.show()
[840]753            if len(self.MAdata) == 1 and self.MAdata[0].domain.classVar and self.MAdata[0].domain.classVar.varType == orange.VarTypes.Discrete:
[188]754                self.classValues = self.MAdata[0].domain.classVar.values.native()
755            else:
756                self.classValues = [str(nd.name) for nd in self.MAdata]
[415]757
[841]758##            selection = QItemSelection()
759##            for cn in range(len(self.classValues)):
760##                item = QListWidgetItem(QIcon(ColorPixmap(self.classBrighterColor[cn])), self.classValues[cn])
761##                self.classQLB.addItem(item)
762##                selection.select(self.classQLB.indexFromItem(item), self.classQLB.indexFromItem(item))
763##            self.classQLB.selectionModel().select(selection, QItemSelectionModel.Select)
764            self.Classes = [(QIcon(ColorPixmap(self.classBrighterColor[cn])), self.classValues[cn])  for cn in range(len(self.classValues))]
765            self.SelectedClasses = range(len(self.Classes))
766           
[415]767##            self.classQLB.selectAll()  ##or: if numberOfClasses > 0: self.classQLB.setSelected(0, 1)
768##            self.update()
[188]769
[840]770##            if len(self.MAdata) == 1 and self.MAdata[0].noclass:
771##                pass
772            self.classQVGB.setDisabled(len(self.MAdata) == 1 and self.MAdata[0].noclass)
[965]773            attrs = self.MAdata[0].domain.variables + self.MAdata[0].domain.getmetas().values()
774            for attr in attrs:
775                self.profileLabelComboBox.addItem(self.attrIcons[attr.varType], attr.name)
776            stringAttrs = [attr for attr in attrs if attr.varType == orange.VarTypes.String]
777            discAttrs = [attr for attr in attrs if attr.varType == orange.VarTypes.Discrete]
778            attrs = stringAttrs[-1:] + discAttrs[-1:] + list(attrs[:1])
779            self.profileLabel = attrs[0].name
[188]780        else:
781            self.classColor = None
782            self.classBrighterColor = None
783            self.classValues = []
[415]784##        self.classQVGB.update()
785##        self.classQVGB.layout().activate()
[188]786        self.graph.show()
[415]787##        self.layout.activate() # this is needed to scale the widget correctly
[188]788
789    def data(self, MAdata, id=None):
790        ## if there is no class attribute, create a dummy one
[841]791        self.closeContext()
[188]792        if MAdata and MAdata.domain.classVar == None:
[840]793##            noClass = orange.EnumVariable('file', values=['n', 'y'])
794##            newDomain = orange.Domain(MAdata.domain.attributes + [noClass])
795##            mname = MAdata.name ## remember name
796##            MAdata = MAdata.select(newDomain) ## because select forgets it
797##            MAdata.name = mname
798##            for e in MAdata: e.setclass('n')
[188]799            MAdata.setattr("noclass", 1) ## remember that there is no class to display
800        elif MAdata and MAdata.domain.classVar.varType <> orange.VarTypes.Discrete:
801            print "error, ignoring table, because its class variable not discrete:", MAdata.domain.classVar
[840]802##            MAdata = None
803            MAdata.setattr("noclass", 1) ## remember that there is no class to display
[188]804        elif MAdata:
805            MAdata.setattr("noclass", 0) ## there are classes by default
806
807        ## handling of more than one data set
808        # check if the same domain
809        if MAdata and len(self.MAdata) and str(MAdata.domain.attributes) <> str(self.MAdata[0].domain.attributes):
810            print "domains:", MAdata.domain.attributes
811            print "and,   :", self.MAdata[0].domain.attributes
812            print "are not same"
[841]813##            MAdata = None
[188]814
815        ids = [d.id for d in self.MAdata]
816        if not MAdata:
817            if id in ids:
818                del self.MAdata[ids.index(id)]
819        else:
820            MAdata.setattr("id", id)
821            if id in ids:
822                MAdata.setattr("id", id)
823                indx = ids.index(id)
824                self.MAdata[indx] = MAdata
825            else:
826                self.MAdata.append(MAdata)
827
828        if len(self.MAdata) == 0:
[415]829            print "hiding graph"
[188]830            self.graph.hide()
831            self.classQVGB.hide()
[840]832            self.infoLabel.setText("No data on input")
[188]833            return
834
835        self.newdata()
[841]836        self.openContext("", MAdata)
837        self.classSelectionChange()
838
839    def sendReport(self):
840        self.startReport("%s" % self.windowTitle())
841        self.reportSettings("Settings", ([("Selected classes" , ",".join(self.Classes[i][1] for i in self.SelectedClasses))] if len(self.Classes) > 1 else []) +\
842                                        [("Show box plot", self.ShowAverageProfile),
843                                         ("Show profiles", self.ShowSingleProfiles)])
844        self.reportRaw("<p>%s</p>" % self.infoLabel.text())
845        self.reportImage(lambda *x: OWChooseImageSizeDlg(self.graph).saveImage(*x))
[965]846       
847    def keyPressEvent(self, event):
848        self.ctrlPressed = event.key() == Qt.Key_Control
849        return OWWidget.keyPressEvent(self, event)
850   
851    def keyReleaseEvent(self, event):
852        self.ctrlPressed = self.ctrlPressed and not event.key() == Qt.Key_Control
853        return OWWidget.keyReleaseEvent(self, event)
854   
855    def commitIf(self):
856        if self.autoSendSelected:
857            self.commit()
858        else:
859            self.selectionChangedFlag = True
860           
861    def commit(self):
862        data = [c.example for c in self.graph.selectedCurves if hasattr(c, "example")]
[1104]863        if data:
864            data = orange.ExampleTable(data)
865        else:
866            data = None
867           
868        self.send("Examples", data)
[965]869        self.selectionChangedFlag = False
870   
[188]871# following is not needed, data handles these cases
872##    def cdata(self, MAcdata):
873##        if not MAcdata:
874##            self.graph.hide()
875##            return
876##        self.MAdata = MAcdata
877##        self.MAnoclass = 0
878##        self.newdata()
879
880if __name__ == "__main__":
881    a = QApplication(sys.argv)
882    owdm = OWDisplayProfiles()
[415]883##    a.setMainWidget(owdm)
[840]884##    d = orange.ExampleTable('e:\\profiles')
[841]885##    d = orange.ExampleTable('e:\\profiles-classes')
[965]886    d = orange.ExampleTable('../../../doc/datasets/brown-selected')
[188]887    print len(d)
888    owdm.data(d)
889    owdm.show()
[415]890    a.exec_()
[188]891    owdm.saveSettings()
Note: See TracBrowser for help on using the repository browser.