source: orange-bioinformatics/_bioinformatics/widgets/OWDisplayProfiles.py @ 1643:2cfa80dac3d3

Revision 1643:2cfa80dac3d3, 37.3 KB checked in by mitar, 2 years ago (diff)

Fixing some imports. Marking widgets as prototypes.

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