source: orange-bioinformatics/widgets/OWDisplayProfiles.py @ 1175:a6c3a3a1eb62

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