source: orange/Orange/OrangeWidgets/Visualize/OWMosaicDisplay.py @ 11096:cf7d2ae9d22b

Revision 11096:cf7d2ae9d22b, 48.2 KB checked in by Ales Erjavec <ales.erjavec@…>, 19 months ago (diff)

Added new svg icons for the widgets/categories.

Line 
1"""
2<name>Mosaic Display</name>
3<description>Shows a mosaic display.</description>
4<contact>Gregor Leban (gregor.leban@fri.uni-lj.si)</contact>
5<icon>icons/MosaicDisplay.svg</icon>
6<priority>4100</priority>
7"""
8# OWMosaicDisplay.py
9#
10from OWWidget import *
11import OWGUI
12from OWMosaicOptimization import *
13from OWTools import getHtmlCompatibleString
14from math import sqrt, floor, ceil, pow
15import operator
16from orngScaleData import getVariableValuesSorted, getVariableValueIndices
17from OWQCanvasFuncts import *
18import OWColorPalette
19import OWDlgs
20from orngVisFuncts import permutations
21from copy import copy
22
23PEARSON = 0
24CLASS_DISTRIBUTION = 1
25
26BOTTOM = 0
27LEFT = 1
28TOP = 2
29RIGHT = 3
30
31class SelectionRectangle(QGraphicsRectItem):
32    pass
33
34class MosaicSceneView(QGraphicsView):
35    def __init__(self, widget, *args):
36        apply(QGraphicsView.__init__,(self,) + args)
37        self.widget = widget
38        self.bMouseDown = False
39        self.mouseDownPosition = QPoint(0,0)
40        self.tempRect = None
41
42    # mouse button was pressed
43    def mousePressEvent(self, ev):
44        QGraphicsView.mousePressEvent(self, ev)
45        self.mouseDownPosition = QPoint(ev.pos().x(), ev.pos().y())
46        self.bMouseDown = True
47        self.mouseMoveEvent(ev)
48
49    # mouse button was pressed and mouse is moving ######################
50    def mouseMoveEvent(self, ev):
51        QGraphicsView.mouseMoveEvent(self, ev)
52        if ev.button() == Qt.RightButton:
53            return
54
55        if not self.bMouseDown:
56            if self.tempRect:
57                self.scene().removeItem(self.tempRect)
58                self.tempRect = None
59        else:
60            if not self.tempRect:
61                self.tempRect = SelectionRectangle(None, self.scene())
62            rect = QRectF(min(self.mouseDownPosition.x(), ev.pos().x()), min (self.mouseDownPosition.y(), ev.pos().y()), max(abs(self.mouseDownPosition.x() - ev.pos().x()),1), max(abs(self.mouseDownPosition.y() - ev.pos().y()),1))
63            self.tempRect.setRect(rect)
64
65
66    # mouse button was released #########################################
67    def mouseReleaseEvent(self, ev):
68        self.bMouseDown = False
69
70        if ev.button() == Qt.RightButton:
71            self.widget.removeLastSelection()
72        elif self.tempRect:
73            self.widget.addSelection(self.tempRect)
74            self.scene().removeItem(self.tempRect)
75            self.tempRect = None
76
77
78class OWMosaicDisplay(OWWidget):
79    settingsList = ["horizontalDistribution", "showAprioriDistributionLines", "showAprioriDistributionBoxes",
80                    "horizontalDistribution", "useBoxes", "interiorColoring", "boxSize", "colorSettings", "selectedSchemaIndex", "cellspace",
81                    "showSubsetDataBoxes", "removeUnusedValues"]
82
83    contextHandlers = {"": DomainContextHandler("", ["attr1", "attr2", "attr3", "attr4", "manualAttributeValuesDict"], loadImperfect = 0)}
84   
85    interiorColoringOpts = ["Standardized (Pearson) residuals", "Class distribution"]
86    subboxesOpts = ["Expected class distribution", "Apriori class distribution"]
87
88    def __init__(self,parent=None, signalManager = None):
89        OWWidget.__init__(self, parent, signalManager, "Mosaic display", True, True)
90
91        #set default settings
92        self.data = None
93        self.unprocessedSubsetData = None
94        self.subsetData = None
95        self.names = []     # class values
96
97        self.inputs = [("Data", ExampleTable, self.setData, Default), ("Data Subset", ExampleTable, self.setSubsetData)]
98        self.outputs = [("Selected Data", ExampleTable), ("Learner", orange.Learner)]
99
100        #load settings
101        self.colorSettings = None
102        self.selectedSchemaIndex = 0
103        self.interiorColoring = 0
104        self.cellspace = 4
105        self.showAprioriDistributionBoxes = 1
106        self.useBoxes = 1
107        self.showSubsetDataBoxes = 1
108        self.horizontalDistribution = 0
109        self.showAprioriDistributionLines = 0
110        self.boxSize = 5
111        self.exploreAttrPermutations = 0
112        self.attr1 = ""
113        self.attr2 = ""
114        self.attr3 = ""
115        self.attr4 = ""
116
117        self.attributeNameOffset = 30
118        self.attributeValueOffset = 15
119        self.residuals = [] # residual values if the residuals are visualized
120        self.aprioriDistributions = []
121        self.colorPalette = None
122        self.permutationDict = {}
123        self.manualAttributeValuesDict = {}
124        self.conditionalDict = None
125        self.conditionalSubsetDict = None
126        self.activeRule = None
127        self.removeUnusedValues = 0
128
129        self.selectionRectangle = None
130        self.selectionConditionsHistorically = []
131        self.selectionConditions = []
132
133        # color paletes for visualizing pearsons residuals
134        #self.blueColors = [QColor(255, 255, 255), QColor(117, 149, 255), QColor(38, 43, 232), QColor(1,5,173)]
135        self.blueColors = [QColor(255, 255, 255), QColor(210, 210, 255), QColor(110, 110, 255), QColor(0,0,255)]
136        self.redColors = [QColor(255, 255, 255), QColor(255, 200, 200), QColor(255, 100, 100), QColor(255, 0, 0)]
137
138        self.loadSettings()
139
140        self.tabs = OWGUI.tabWidget(self.controlArea)
141        self.GeneralTab = OWGUI.createTabPage(self.tabs, "Main")
142        self.SettingsTab = OWGUI.createTabPage(self.tabs, "Settings")
143
144        self.canvas = QGraphicsScene()
145        self.canvasView = MosaicSceneView(self, self.canvas, self.mainArea)
146        self.mainArea.layout().addWidget(self.canvasView)
147        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
148        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
149        self.canvasView.setRenderHint(QPainter.Antialiasing)
150        #self.canvasView.setAlignment(Qt.AlignLeft | Qt.AlignTop)
151
152        #GUI
153        #add controls to self.controlArea widget
154        #self.controlArea.setMinimumWidth(235)
155
156        texts = ["1st Attribute", "2nd Attribute", "3rd Attribute", "4th Attribute"]
157        for i in range(1,5):
158            box = OWGUI.widgetBox(self.GeneralTab, texts[i-1], orientation = "horizontal")
159            combo = OWGUI.comboBox(box, self, "attr" + str(i), None, callback = self.updateGraphAndPermList, sendSelectedValue = 1, valueType = str)
160
161            butt = OWGUI.button(box, self, "", callback = self.orderAttributeValues, tooltip = "Change the order of attribute values", debuggingEnabled = 0)
162            butt.setFixedSize(26, 24)
163            butt.setCheckable(1)
164            butt.setIcon(QIcon(os.path.join(self.widgetDir, "icons/Dlg_sort.png")))
165
166            setattr(self, "sort"+str(i), butt)
167            setattr(self, "attr" + str(i)+ "Combo", combo)
168
169        self.optimizationDlg = OWMosaicOptimization(self, self.signalManager)
170
171        optimizationButtons = OWGUI.widgetBox(self.GeneralTab, "Dialogs", orientation = "horizontal")
172        OWGUI.button(optimizationButtons, self, "VizRank", callback = self.optimizationDlg.reshow, debuggingEnabled = 0, tooltip = "Find attribute combinations that will separate different classes as clearly as possible.")
173
174        self.collapsableWBox = OWGUI.collapsableWidgetBox(self.GeneralTab, "Explore Attribute Permutations", self, "exploreAttrPermutations", callback = self.permutationListToggle)
175        self.permutationList = OWGUI.listBox(self.collapsableWBox, self, callback = self.setSelectedPermutation)
176        #self.permutationList.hide()
177        self.GeneralTab.layout().addStretch(100)
178
179        # ######################
180        # SETTINGS TAB
181        # ######################
182        box5 = OWGUI.widgetBox(self.SettingsTab, "Colors in Cells Represent...", addSpace = 1)
183        OWGUI.comboBox(box5, self, "interiorColoring", None, items = self.interiorColoringOpts, callback = self.updateGraph)
184        #box5.setSizePolicy(QSizePolicy(QSizePolicy.Minimum , QSizePolicy.Fixed ))
185
186        box = OWGUI.widgetBox(self.SettingsTab, "Visual Settings", addSpace = 1)
187       
188        OWGUI.hSlider(box, self, 'cellspace', label = "Cell distance: ", minValue=1, maxValue=15, step=1, callback = self.updateGraph, tooltip = "What is the minimum distance between two rectangles in the plot?")
189        OWGUI.checkBox(box, self, "removeUnusedValues", "Remove unused attribute values", tooltip = "Do you want to remove unused attribute values?\nThis setting will not be considered until new data is received.")
190
191        self.box6 = OWGUI.widgetBox(self.SettingsTab, "Cell Distribution Settings", addSpace = 1)
192        OWGUI.comboBox(self.box6, self, 'horizontalDistribution', items = ["Show Distribution Vertically", "Show Distribution Horizontally"], tooltip = "Do you wish to see class distribution drawn horizontally or vertically?", callback = self.updateGraph)
193        OWGUI.checkBox(self.box6, self, 'showAprioriDistributionLines', 'Show apriori distribution with lines', callback = self.updateGraph, tooltip = "Show the lines that represent the apriori class distribution")
194
195        self.box8 = OWGUI.widgetBox(self.SettingsTab, "Boxes in Cells", addSpace = 1)
196        OWGUI.hSlider(self.box8, self, 'boxSize', label = "Size: ", minValue=1, maxValue=15, step=1, callback = self.updateGraph, tooltip = "What is the size of the boxes on the left and right edge of each cell?")
197        OWGUI.checkBox(self.box8, self, 'showSubsetDataBoxes', 'Show class distribution of subset data', callback = self.updateGraph, tooltip = "Show small boxes at right (or bottom) edge of cells to represent class distribution of examples from example subset input.")
198        cb = OWGUI.checkBox(self.box8, self, 'useBoxes', 'Use boxes on left to show...', callback = self.updateGraph, tooltip = "Show small boxes at left (or top) edge of cells to represent additional information.")
199        indBox = OWGUI.indentedBox(self.box8, sep=OWGUI.checkButtonOffsetHint(cb))
200        OWGUI.comboBox(indBox, self, 'showAprioriDistributionBoxes', items = self.subboxesOpts, tooltip = "Show additional boxes for each mosaic cell representing:\n - expected class distribution (assuming independence between attributes)\n - apriori class distribution (based on all examples).", callback = self.updateGraph)
201
202        hbox = OWGUI.widgetBox(self.SettingsTab, "Colors", addSpace = 1)
203        OWGUI.button(hbox, self, "Set Colors", self.setColors, tooltip = "Set the color palette for class values", debuggingEnabled = 0)
204
205        #self.box6.setSizePolicy(QSizePolicy(QSizePolicy.Minimum , QSizePolicy.Fixed ))
206        self.SettingsTab.layout().addStretch(1)
207
208        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveToFileCanvas)
209        self.icons = self.createAttributeIconDict()
210        self.resize(830, 550)
211
212        self.VizRankLearner = MosaicTreeLearner(self.optimizationDlg)
213        self.send("Learner", self.VizRankLearner)
214
215        self.wdChildDialogs = [self.optimizationDlg]        # used when running widget debugging
216
217        self.collapsableWBox.updateControls()
218        dlg = self.createColorDialog()
219        self.colorPalette = dlg.getDiscretePalette("discPalette")
220        self.selectionColorPalette = [QColor(*col) for col in OWColorPalette.defaultRGBColors]
221
222
223    def sendReport(self):
224        self.reportSettings("Settings", [("Color in cells", self.interiorColoringOpts[self.interiorColoring]),
225                                         ("Subboxes", self.subboxesOpts[self.useBoxes])])
226        self.reportImage(lambda *x: OWDlgs.OWChooseImageSizeDlg(self.canvas).saveImage(*x))
227
228    def permutationListToggle(self):
229        if self.exploreAttrPermutations:
230            self.updateGraphAndPermList()
231
232    def setSelectedPermutation(self):
233        newRow = self.permutationList.currentRow()
234        if self.permutationList.count() > 0 and self.bestPlacements and newRow < len(self.bestPlacements):
235            self.removeAllSelections()
236            val, attrList, valueOrder = self.bestPlacements[newRow]
237            if len(attrList) > 0: self.attr1 = attrList[0]
238            if len(attrList) > 1: self.attr2 = attrList[1]
239            if len(attrList) > 2: self.attr3 = attrList[2]
240            if len(attrList) > 3: self.attr4 = attrList[3]
241            self.updateGraph(customValueOrderDict = dict([(attrList[i], tuple(valueOrder[i])) for i in range(len(attrList))]))
242
243    def orderAttributeValues(self):
244        attr = None
245        if self.sort1.isChecked():   attr = self.attr1
246        elif self.sort2.isChecked(): attr = self.attr2
247        elif self.sort3.isChecked(): attr = self.attr3
248        elif self.sort4.isChecked(): attr = self.attr4
249
250        if self.data and attr  != "" and attr != "(None)":
251            dlg = SortAttributeValuesDlg(self, attr, self.manualAttributeValuesDict.get(attr, None) or getVariableValuesSorted(self.data.domain[attr]))
252            if dlg.exec_() == QDialog.Accepted:
253                self.manualAttributeValuesDict[attr] = [str(dlg.attributeList.item(i).text()) for i in range(dlg.attributeList.count())]
254
255        for control in [self.sort1, self.sort2, self.sort3, self.sort4]:
256            control.setChecked(0)
257        self.updateGraph()
258
259    # initialize combo boxes with discrete attributes
260    def initCombos(self, data):
261        for combo in [self.attr1Combo, self.attr2Combo, self.attr3Combo, self.attr4Combo]:
262            combo.clear()
263
264        if data == None: return
265
266        self.attr2Combo.addItem("(None)")
267        self.attr3Combo.addItem("(None)")
268        self.attr4Combo.addItem("(None)")
269
270        for attr in data.domain:
271            if attr.varType == orange.VarTypes.Discrete:
272                for combo in [self.attr1Combo, self.attr2Combo, self.attr3Combo, self.attr4Combo]:
273                    combo.addItem(self.icons[orange.VarTypes.Discrete], attr.name)
274
275        if self.attr1Combo.count() > 0:
276            self.attr1 = str(self.attr1Combo.itemText(0))
277            self.attr2 = str(self.attr2Combo.itemText(0 + 2*(self.attr2Combo.count() > 2)))
278        self.attr3 = str(self.attr3Combo.itemText(0))
279        self.attr4 = str(self.attr4Combo.itemText(0))
280
281    #  when we resize the widget, we have to redraw the data
282    def resizeEvent(self, e):
283        OWWidget.resizeEvent(self,e)
284        self.updateGraph()
285
286    def showEvent(self, ev):
287        OWWidget.showEvent(self, ev)
288        self.updateGraph()
289
290    def closeEvent(self, ce):
291        self.optimizationDlg.hide()
292        QDialog.closeEvent(self, ce)
293
294    # ------------- SIGNALS --------------------------
295    # # DATA signal - receive new data and update all fields
296    def setData(self, data):
297        self.closeContext()
298        self.data = None
299        self.bestPlacements = None
300        self.manualAttributeValuesDict = {}
301        self.attributeValuesDict = {}
302        self.information([0,1,2])
303
304        self.data = self.optimizationDlg.setData(data, self.removeUnusedValues)
305
306        if self.data:
307            if data.domain.hasContinuousAttributes():
308                self.information(0, "Continuous attributes were discretized using entropy discretization.")
309            if data.domain.classVar and data.hasMissingClasses():
310                self.information(1, "Examples with missing classes were removed.")
311#            if self.removeUnusedValues and len(data) != len(self.data):
312#                self.information(2, "Unused attribute values were removed.")
313
314            if self.data.domain.classVar and self.data.domain.classVar.varType == orange.VarTypes.Discrete:
315                self.interiorColoring = CLASS_DISTRIBUTION
316                self.colorPalette.setNumberOfColors(len(self.data.domain.classVar.values))
317            else:
318                self.interiorColoring = PEARSON
319
320        self.initCombos(self.data)
321        self.openContext("", self.data)
322
323
324        if data and self.unprocessedSubsetData:        # if we first received subset data we now have to call setSubsetData to process it
325            self.setSubsetData(self.unprocessedSubsetData)
326            self.unprocessedSubsetData = None
327
328    def setSubsetData(self, data):
329        if not self.data:
330            self.unprocessedSubsetData = data
331            self.warning(10)
332        else:
333            try:
334                self.subsetData = data.select(self.data.domain)
335                self.warning(10)
336            except:
337                self.subsetData = None
338                self.warning(10, data and "'Examples' and 'Example Subset' data do not have compatible domains. Unable to draw 'Example Subset' data." or "")
339
340
341    # this is called by OWBaseWidget after setData and setSubsetData are called. this way the graph is updated only once
342    def handleNewSignals(self):
343        self.updateGraphAndPermList()
344
345    # ------------------------------------------------
346
347    def setShownAttributes(self, attrList, **args):
348        if not attrList: return
349        self.attr1 = attrList[0]
350
351        if len(attrList) > 1: self.attr2 = attrList[1]
352        else: self.attr2 = "(None)"
353
354        if len(attrList) > 2: self.attr3 = attrList[2]
355        else: self.attr3 = "(None)"
356
357        if len(attrList) > 3: self.attr4 = attrList[3]
358        else: self.attr4 = "(None)"
359
360        self.attributeValuesDict = args.get("customValueOrderDict", None)
361        self.updateGraphAndPermList()
362
363    def getShownAttributeList(self):
364        attrList = [self.attr1, self.attr2, self.attr3, self.attr4]
365        while "(None)" in attrList: attrList.remove("(None)")
366        while "" in attrList:       attrList.remove("")
367        return attrList
368
369    def updateGraphAndPermList(self, **args):
370        self.removeAllSelections()
371        self.permutationList.clear()
372
373        if self.exploreAttrPermutations:
374            attrList = self.getShownAttributeList()
375            if not getattr(self, "bestPlacements", []) or 0 in [attr in self.bestPlacements[0][1] for attr in attrList]:        # we might have bestPlacements for a different set of attributes
376                self.setStatusBarText("Evaluating different attribute permutations. You can stop evaluation by opening VizRank dialog and pressing 'Stop optimization' button.")
377                self.bestPlacements = self.optimizationDlg.optimizeCurrentAttributeOrder(attrList, updateGraph = 0)
378                self.setStatusBarText("")
379
380            if self.bestPlacements:
381                self.permutationList.addItems(["%.2f - %s" % (val, attrs) for (val, attrs, order) in self.bestPlacements])
382                attrList, valueOrder = self.bestPlacements[0][1], self.bestPlacements[0][2]
383                self.attributeValuesDict = dict([(attrList[i], tuple(valueOrder[i])) for i in range(len(attrList))])
384
385        self.updateGraph(**args)
386
387    # ############################################################################
388    # updateGraph - gets called every time the graph has to be updated
389    def updateGraph(self, data = -1, subsetData = -1, attrList = -1, **args):
390        # do we want to erase previous diagram?
391        if args.get("erasePrevious", 1):
392            for item in self.canvas.items():
393                if not isinstance(item, SelectionRectangle):
394                    self.canvas.removeItem(item)    # remove all canvas items, except SelectionCurves
395            self.names = []
396
397        if data == -1:
398            data = self.data
399
400        if subsetData == -1:
401            subsetData = self.subsetData
402
403        if attrList == -1:
404            attrList = [self.attr1, self.attr2, self.attr3, self.attr4]
405
406        if data == None : return
407
408        while "(None)" in attrList: attrList.remove("(None)")
409        while "" in attrList:       attrList.remove("")
410        if attrList == []:
411            return
412
413        selectList = attrList
414        if data.domain.classVar:
415            data = data.select(attrList + [data.domain.classVar])
416        else:
417            data = data.select(attrList)
418        data = orange.Preprocessor_dropMissing(data)
419
420        if len(data) == 0:
421            self.warning(5, "There are no examples with valid values for currently visualized attributes. Unable to visualize.")
422            return
423        else:
424            self.warning(5)
425
426        self.aprioriDistributions = []
427        if self.interiorColoring == PEARSON:
428            self.aprioriDistributions = [orange.Distribution(attr, data) for attr in attrList]
429
430        if args.get("positions"):
431            xOff, yOff, squareSize = args.get("positions")
432        else:
433            # get the maximum width of rectangle
434            xOff = 50
435            width = 50
436            if len(attrList) > 1:
437                text = OWCanvasText(self.canvas, attrList[1], bold = 1, show = 0)
438                width = text.boundingRect().width() + 30 + 20
439                xOff = width
440                if len(attrList) == 4:
441                    text = OWCanvasText(self.canvas, attrList[3], bold = 1, show = 0)
442                    width += text.boundingRect().width() + 30 + 20
443
444            # get the maximum height of rectangle
445            height = 90
446            yOff = 40
447            squareSize = min(self.canvasView.width() - width - 20, self.canvasView.height() - height - 20)
448
449        if squareSize < 0: return    # canvas is too small to draw rectangles
450        self.canvasView.setSceneRect(0, 0, self.canvasView.width(), self.canvasView.height())
451
452        self.legend = {}        # dictionary that tells us, for what attributes did we already show the legend
453        for attr in attrList:
454            self.legend[attr] = 0
455
456        self.drawnSides = dict([(0,0),(1,0),(2,0),(3,0)])
457        self.drawPositions = {}
458
459        if not getattr(self, "attributeValuesDict", None):
460            self.attributeValuesDict = self.manualAttributeValuesDict
461
462        # compute distributions
463        self.conditionalDict = self.optimizationDlg.getConditionalDistributions(data, attrList)
464        self.conditionalDict[""] = len(data)
465        self.conditionalSubsetDict = None
466        if subsetData:
467            self.conditionalSubsetDict = self.optimizationDlg.getConditionalDistributions(subsetData, attrList)
468            self.conditionalSubsetDict[""] = len(subsetData)
469
470        # draw rectangles
471        self.DrawData(attrList, (xOff, xOff+squareSize), (yOff, yOff+squareSize), 0, "", len(attrList), **args)
472        if args.get("drawLegend", 1):
473            self.DrawLegend(data, (xOff, xOff+squareSize), (yOff, yOff+squareSize)) # draw class legend
474
475        if args.get("drillUpdateSelection", 1):
476            self.optimizationDlg.mtUpdateState()
477
478        #self.canvas.update()
479
480
481    # ############################################################################
482    # ############################################################################
483
484    ##  DRAW DATA - draw rectangles for attributes in attrList inside rect (x0,x1), (y0,y1)
485    def DrawData(self, attrList, (x0, x1), (y0, y1), side, condition, totalAttrs, usedAttrs = [], usedVals = [], attrVals = "", **args):
486        if self.conditionalDict[attrVals] == 0:
487            self.addRect(x0, x1, y0, y1, "", usedAttrs, usedVals, attrVals = attrVals)
488            self.DrawText(side, attrList[0], (x0, x1), (y0, y1), totalAttrs, usedAttrs, usedVals, attrVals)  # store coordinates for later drawing of labels
489            return
490
491        attr = attrList[0]
492        edge = len(attrList) * self.cellspace  # how much smaller rectangles do we draw
493        values = self.attributeValuesDict.get(attr, None) or getVariableValuesSorted(self.data.domain[attr])
494        if side%2: values = values[::-1]        # reverse names if necessary
495
496        if side%2 == 0:                                     # we are drawing on the x axis
497            whole = max(0, (x1-x0)-edge*(len(values)-1))  # we remove the space needed for separating different attr. values
498            if whole == 0: edge = (x1-x0)/float(len(values)-1)
499        else:                                               # we are drawing on the y axis
500            whole = max(0, (y1-y0)-edge*(len(values)-1))
501            if whole == 0: edge = (y1-y0)/float(len(values)-1)
502
503        if attrVals == "": counts = [self.conditionalDict[val] for val in values]
504        else:              counts = [self.conditionalDict[attrVals + "-" + val] for val in values]
505        total = sum(counts)
506
507        # if we are visualizing the third attribute and the first attribute has the last value, we have to reverse the order in which the boxes will be drawn
508        # otherwise, if the last cell, nearest to the labels of the fourth attribute, is empty, we wouldn't be able to position the labels
509        valRange = range(len(values))
510        if len(attrList + usedAttrs) == 4 and len(usedAttrs) == 2:
511            attr1Values = self.attributeValuesDict.get(usedAttrs[0], None) or getVariableValuesSorted(self.data.domain[usedAttrs[0]])
512            if usedVals[0] == attr1Values[-1]:
513                valRange = valRange[::-1]
514
515        for i in valRange:
516            start = i*edge + whole * float(sum(counts[:i])/float(total))
517            end   = i*edge + whole * float(sum(counts[:i+1])/float(total))
518            val = values[i]
519            htmlVal = getHtmlCompatibleString(val)
520            if attrVals != "": newAttrVals = attrVals + "-" + val
521            else:              newAttrVals = val
522
523            if side % 2 == 0:   # if we are moving horizontally
524                if len(attrList) == 1:  self.addRect(x0+start, x0+end, y0, y1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals, **args)
525                else:                   self.DrawData(attrList[1:], (x0+start, x0+end), (y0, y1), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, usedAttrs + [attr], usedVals + [val], newAttrVals, **args)
526            else:
527                if len(attrList) == 1:  self.addRect(x0, x1, y0+start, y0+end, condition + 4*"&nbsp;" + attr + ": <b> " + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals, **args)
528                else:                   self.DrawData(attrList[1:], (x0, x1), (y0+start, y0+end), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, usedAttrs + [attr], usedVals + [val], newAttrVals, **args)
529
530        self.DrawText(side, attrList[0], (x0, x1), (y0, y1), totalAttrs, usedAttrs, usedVals, attrVals)
531
532
533    ######################################################################
534    ## DRAW TEXT - draw legend for all attributes in attrList and their possible values
535    def DrawText(self, side, attr, (x0, x1), (y0, y1), totalAttrs, usedAttrs, usedVals, attrVals):
536        if self.drawnSides[side]: return
537
538        # the text on the right will be drawn when we are processing visualization of the last value of the first attribute
539        if side == RIGHT:
540            attr1Values = self.attributeValuesDict.get(usedAttrs[0], None) or getVariableValuesSorted(self.data.domain[usedAttrs[0]])
541            if usedVals[0] != attr1Values[-1]:
542                return
543
544        if not self.conditionalDict[attrVals]:
545            if not self.drawPositions.has_key(side): self.drawPositions[side] = (x0, x1, y0, y1)
546            return
547        else:
548            if self.drawPositions.has_key(side): (x0, x1, y0, y1) = self.drawPositions[side]        # restore the positions where we have to draw the attribute values and attribute name
549
550        self.drawnSides[side] = 1
551
552        values = self.attributeValuesDict.get(attr, None) or getVariableValuesSorted(self.data.domain[attr])
553        if side % 2:  values = values[::-1]
554
555        width  = x1-x0 - (side % 2 == 0) * self.cellspace*(totalAttrs-side)*(len(values)-1)
556        height = y1-y0 - (side % 2 == 1) * self.cellspace*(totalAttrs-side)*(len(values)-1)
557
558        #calculate position of first attribute
559        if side == 0:    OWCanvasText(self.canvas, attr, x0+(x1-x0)/2, y1 + self.attributeNameOffset, Qt.AlignCenter, bold = 1)
560        elif side == 1:  OWCanvasText(self.canvas, attr, x0 - self.attributeNameOffset, y0+(y1-y0)/2, Qt.AlignRight | Qt.AlignVCenter, bold = 1)
561        elif side == 2:  OWCanvasText(self.canvas, attr, x0+(x1-x0)/2, y0 - self.attributeNameOffset, Qt.AlignCenter, bold = 1)
562        else:            OWCanvasText(self.canvas, attr, x1 + self.attributeNameOffset, y0+(y1-y0)/2, Qt.AlignLeft | Qt.AlignVCenter, bold = 1)
563
564        currPos = 0
565
566        if attrVals == "":  counts = [self.conditionalDict.get(val, 1) for val in values]
567        else:               counts = [self.conditionalDict.get(attrVals + "-" + val, 1) for val in values]
568        total = sum(counts)
569        if total == 0:
570            counts = [1]*len(values)
571            total = sum(counts)
572
573        for i in range(len(values)):
574            val = values[i]
575            perc = counts[i]/float(total)
576            if side == 0:    OWCanvasText(self.canvas, str(val), x0+currPos+width*0.5*perc, y1 + self.attributeValueOffset, Qt.AlignCenter, bold = 0)
577            elif side == 1:  OWCanvasText(self.canvas, str(val), x0-self.attributeValueOffset, y0+currPos+height*0.5*perc, Qt.AlignRight | Qt.AlignVCenter, bold = 0)
578            elif side == 2:  OWCanvasText(self.canvas, str(val), x0+currPos+width*perc*0.5, y0 - self.attributeValueOffset, Qt.AlignCenter, bold = 0)
579            else:            OWCanvasText(self.canvas, str(val), x1+self.attributeValueOffset, y0 + currPos + height*0.5*perc, Qt.AlignLeft | Qt.AlignVCenter, bold = 0)
580
581            if side % 2 == 0: currPos += perc*width + self.cellspace*(totalAttrs-side)
582            else :            currPos += perc*height+ self.cellspace*(totalAttrs-side)
583
584    # draw a rectangle, set it to back and add it to rect list
585    def addRect(self, x0, x1, y0, y1, condition = "", usedAttrs = [], usedVals = [], attrVals = "", **args):
586        if x0 == x1: x1+=1
587        if y0 == y1: y1+=1
588
589        if x1-x0 + y1-y0 == 2: y1+=1        # if we want to show a rectangle of width and height 1 it doesn't show anything. in such cases we therefore have to increase size of one edge
590
591        if args.has_key("selectionDict") and args["selectionDict"].has_key(tuple(usedVals)):
592            d = 2
593            OWCanvasRectangle(self.canvas, x0-d, y0-d, x1-x0+1+2*d, y1-y0+1+2*d, penColor = args["selectionDict"][tuple(usedVals)], penWidth = 2, z = -100)
594
595        # if we have selected a rule that contains this combination of attr values then show a kind of selection of this rectangle
596        if self.activeRule and len(usedAttrs) == len(self.activeRule[0]) and sum([v in usedAttrs for v in self.activeRule[0]]) == len(self.activeRule[0]):
597            for vals in self.activeRule[1]:
598                if usedVals == [vals[self.activeRule[0].index(a)] for a in usedAttrs]:
599                    values = list(self.attributeValuesDict.get(self.data.domain.classVar.name, [])) or getVariableValuesSorted(self.data.domain.classVar)
600                    counts = [self.conditionalDict[attrVals + "-" + val] for val in values]
601                    d = 2
602                    r = OWCanvasRectangle(self.canvas, x0-d, y0-d, x1-x0+2*d+1, y1-y0+2*d+1, z = 50)
603                    r.setPen(QPen(self.colorPalette[counts.index(max(counts))], 2, Qt.DashLine))
604
605        aprioriDist = None; pearson = None; expected = None
606        outerRect = OWCanvasRectangle(self.canvas, x0, y0, x1-x0, y1-y0, z = 30)
607
608        if not self.conditionalDict[attrVals]: return
609
610        # we have to remember which conditions were new in this update so that when we right click we can only remove the last added selections
611        if self.selectionRectangle != None and self.selectionRectangle.collidesWithItem(outerRect) and tuple(usedVals) not in self.selectionConditions:
612            self.recentlyAdded = getattr(self, "recentlyAdded", []) + [tuple(usedVals)]
613            self.selectionConditions = self.selectionConditions + [tuple(usedVals)]
614
615        # show rectangle selected or not
616        if tuple(usedVals) in self.selectionConditions:
617            outerRect.setPen(QPen(Qt.black, 3, Qt.DotLine))
618
619        if self.interiorColoring == CLASS_DISTRIBUTION and (not self.data.domain.classVar or not self.data.domain.classVar.varType == orange.VarTypes.Discrete):
620            return
621
622        # draw pearsons residuals
623        if self.interiorColoring == PEARSON or not self.data.domain.classVar or self.data.domain.classVar.varType != orange.VarTypes.Discrete:
624            s = sum(self.aprioriDistributions[0])
625            expected = s * reduce(lambda x, y: x*y, [self.aprioriDistributions[i][usedVals[i]]/float(s) for i in range(len(usedVals))])
626            actual = self.conditionalDict[attrVals]
627            pearson = float(actual - expected) / sqrt(expected)
628            if abs(pearson) < 2:   ind = 0
629            elif abs(pearson) < 4: ind = 1
630            elif abs(pearson) < 8: ind = 2
631            else:                  ind = 3
632
633            if pearson > 0: color = self.blueColors[ind]
634            else: color = self.redColors[ind]
635            OWCanvasRectangle(self.canvas, x0, y0, x1-x0, y1-y0, color, color, z = -20)
636
637        # draw class distribution - actual and apriori
638        # we do have a discrete class
639        else:
640            clsValues = list(self.attributeValuesDict.get(self.data.domain.classVar.name, [])) or getVariableValuesSorted(self.data.domain.classVar)
641            aprioriDist = orange.Distribution(self.data.domain.classVar.name, self.data)
642            total = 0
643            for i in range(len(clsValues)):
644                val = self.conditionalDict[attrVals + "-" + clsValues[i]]
645                if val == 0:
646                    continue
647                if self.horizontalDistribution:
648                    if i == len(clsValues)-1: v = x1-x0 - total
649                    else:                       v = ((x1-x0)* val)/self.conditionalDict[attrVals]
650                    OWCanvasRectangle(self.canvas, x0+total, y0, v, y1-y0, self.colorPalette[i], self.colorPalette[i], z = -20)
651                else:
652                    if i == len(clsValues)-1: v = y1-y0 - total
653                    else:                       v = ((y1-y0)* val)/self.conditionalDict[attrVals]
654                    OWCanvasRectangle(self.canvas, x0, y0+total, x1-x0, v, self.colorPalette[i], self.colorPalette[i], z = -20)
655                total += v
656
657            # show apriori boxes and lines
658            if (self.showAprioriDistributionLines or self.useBoxes) and abs(x1 - x0) > self.boxSize and abs(y1 - y0) > self.boxSize:
659                apriori = [aprioriDist[val]/float(len(self.data)) for val in clsValues]
660                if self.showAprioriDistributionBoxes or self.data.domain.classVar.name in usedAttrs:   # we want to show expected class distribution under independence hypothesis
661                    boxCounts = apriori
662                else:
663                    contingencies = self.optimizationDlg.getContingencys(usedAttrs)
664                    boxCounts = []
665                    for clsVal in clsValues:
666                        # compute: P(c_i) * prod (P(c_i|attr_k) / P(c_i))  for each class value
667                        Pci = aprioriDist[clsVal]/float(sum(aprioriDist.values()))
668                        tempVal = Pci
669                        if Pci > 0:
670                            #tempVal = 1.0 / Pci
671                            for i in range(len(usedAttrs)):
672                                tempVal *= contingencies[usedAttrs[i]][usedVals[i]][clsVal] / Pci
673                        boxCounts.append(tempVal)
674                        #boxCounts.append(aprioriDist[val]/float(sum(aprioriDist.values())) * reduce(operator.mul, [contingencies[usedAttrs[i]][usedVals[i]][clsVal]/float(sum(contingencies[usedAttrs[i]][usedVals[i]].values())) for i in range(len(usedAttrs))]))
675
676                total1 = 0; total2 = 0
677                if self.useBoxes:
678                    if self.horizontalDistribution:  OWCanvasLine(self.canvas, x0, y0+self.boxSize, x1, y0+self.boxSize, z = 30)
679                    else:                            OWCanvasLine(self.canvas, x0+self.boxSize, y0, x0+self.boxSize, y1, z = 30)
680
681                for i in range(len(clsValues)):
682                    val1 = apriori[i]
683                    if self.showAprioriDistributionBoxes: val2 = apriori[i]
684                    else:                                 val2 = boxCounts[i]/float(sum(boxCounts))
685                    if self.horizontalDistribution:
686                        if i == len(clsValues)-1:
687                            v1 = x1-x0 - total1
688                            v2 = x1-x0 - total2
689                        else:
690                            v1 = (x1-x0)* val1
691                            v2 = (x1-x0)* val2
692                        x,y,w,h, xL1, yL1, xL2, yL2 = x0+total2, y0, v2, self.boxSize, x0+total1+v1, y0, x0+total1+v1, y1
693                    else:
694                        if i== len(clsValues)-1:
695                            v1 = y1-y0 - total1
696                            v2 = y1-y0 - total2
697                        else:
698                            v1 = (y1-y0)* val1
699                            v2 = (y1-y0)* val2
700                        x,y,w,h, xL1, yL1, xL2, yL2 = x0, y0+total2, self.boxSize, v2, x0, y0+total1+v1, x1, y0+total1+v1
701
702                    if self.useBoxes:
703                        OWCanvasRectangle(self.canvas, x, y, w, h, self.colorPalette[i], self.colorPalette[i], z = 20)
704                    if i < len(clsValues)-1 and self.showAprioriDistributionLines:
705                        OWCanvasLine(self.canvas, xL1, yL1, xL2, yL2, z = 10)
706
707                    total1 += v1
708                    total2 += v2
709
710            # show subset distribution
711            if self.conditionalSubsetDict:
712                # show a rect around the box if subset examples belong to this box
713                if self.conditionalSubsetDict[attrVals]:
714                    #counts = [self.conditionalSubsetDict[attrVals + "-" + val] for val in clsValues]
715                    #if sum(counts) == 1:    color = self.colorPalette[counts.index(1)]
716                    #else:                   color = Qt.black
717                    #OWCanvasRectangle(self.canvas, x0-2, y0-2, x1-x0+5, y1-y0+5, color, QColor(Qt.white), penWidth = 2, z=-50, penStyle = Qt.DashLine)
718                    counts = [self.conditionalSubsetDict[attrVals + "-" + val] for val in clsValues]
719                    if sum(counts) == 1:
720                        OWCanvasRectangle(self.canvas, x0-2, y0-2, x1-x0+5, y1-y0+5, self.colorPalette[counts.index(1)], QColor(Qt.white), penWidth = 2, z=-50, penStyle = Qt.DashLine)
721
722                    if self.showSubsetDataBoxes:     # do we want to show exact distribution in the right edge of each cell
723                        if self.horizontalDistribution:  OWCanvasLine(self.canvas, x0, y1-self.boxSize, x1, y1-self.boxSize, z = 30)
724                        else:                            OWCanvasLine(self.canvas, x1-self.boxSize, y0, x1-self.boxSize, y1, z = 30)
725                        total = 0
726                        for i in range(len(aprioriDist)):
727                            val = self.conditionalSubsetDict[attrVals + "-" + clsValues[i]]
728                            if not self.conditionalSubsetDict[attrVals] or val == 0: continue
729                            if self.horizontalDistribution:
730                                if i == len(aprioriDist)-1: v = x1-x0 - total
731                                else:                       v = ((x1-x0)* val)/float(self.conditionalSubsetDict[attrVals])
732                                OWCanvasRectangle(self.canvas, x0+total, y1-self.boxSize, v, self.boxSize, self.colorPalette[i], self.colorPalette[i], z = 15)
733                            else:
734                                if i == len(aprioriDist)-1: v = y1-y0 - total
735                                else:                       v = ((y1-y0)* val)/float(self.conditionalSubsetDict[attrVals])
736                                OWCanvasRectangle(self.canvas, x1-self.boxSize, y0+total, self.boxSize, v, self.colorPalette[i], self.colorPalette[i], z = 15)
737                            total += v
738
739        tooltipText = "Examples in this area have:<br>" + condition
740
741        if aprioriDist:
742            clsValues = list(self.attributeValuesDict.get(self.data.domain.classVar.name, [])) or getVariableValuesSorted(self.data.domain.classVar)
743            actual = [self.conditionalDict[attrVals + "-" + clsValues[i]] for i in range(len(aprioriDist))]
744            if sum(actual) > 0:
745                apriori = [aprioriDist[key] for key in clsValues]
746                aprioriText = ""; actualText = ""
747                text = ""
748                for i in range(len(clsValues)):
749                    text += 4*"&nbsp;" + "<b>%s</b>: %d / %.1f%% (Expected %.1f / %.1f%%)<br>" % (clsValues[i], actual[i], 100.0*actual[i]/float(sum(actual)), (apriori[i]*sum(actual))/float(sum(apriori)), 100.0*apriori[i]/float(sum(apriori)))
750                tooltipText += "Number of examples: " + str(int(sum(actual))) + "<br> Class distribution:<br>" + text[:-4]
751        elif pearson and expected:
752            tooltipText += "<hr>Expected number of examples: %.1f<br>Actual number of examples: %d<br>Standardized (Pearson) residual: %.1f" % (expected, self.conditionalDict[attrVals], pearson)
753        outerRect.setToolTip(tooltipText)
754
755
756    # draw the class legend below the square
757    def DrawLegend(self, data, (x0, x1), (y0, y1)):
758        if self.interiorColoring == CLASS_DISTRIBUTION and (not data.domain.classVar or data.domain.classVar.varType == orange.VarTypes.Continuous):
759            return
760
761        if self.interiorColoring == PEARSON:
762            names = ["<-8", "-8:-4", "-4:-2", "-2:2", "2:4", "4:8", ">8", "Residuals:"]
763            colors = self.redColors[::-1] + self.blueColors[1:]
764        else:
765            names = (list(self.attributeValuesDict.get(data.domain.classVar.name, [])) or getVariableValuesSorted(data.domain.classVar)) + [data.domain.classVar.name+":"]
766            colors = [self.colorPalette[i] for i in range(len(data.domain.classVar.values))]
767
768        self.names = [OWCanvasText(self.canvas, name, alignment = Qt.AlignVCenter) for name in names]
769        totalWidth = sum([text.boundingRect().width() for text in self.names])
770
771        # compute the x position of the center of the legend
772        y = y1 + self.attributeNameOffset + 20
773        distance = 30
774        startX = (x0+x1)/2 - (totalWidth + (len(names))*distance)/2
775
776        self.names[-1].setPos(startX+15, y); self.names[-1].show()
777        xOffset = self.names[-1].boundingRect().width() + distance
778
779        size = 8 # 8 + 8*(self.interiorColoring == PEARSON)
780
781        for i in range(len(names)-1):
782            if self.interiorColoring == PEARSON: edgeColor = Qt.black
783            else: edgeColor = colors[i]
784
785            OWCanvasRectangle(self.canvas, startX + xOffset, y-size/2, size, size, edgeColor, colors[i])
786            self.names[i].setPos(startX + xOffset + 10, y)
787            xOffset += distance + self.names[i].boundingRect().width()
788
789    def saveToFileCanvas(self):
790        sizeDlg = OWDlgs.OWChooseImageSizeDlg(self.canvas, parent=self)
791        sizeDlg.exec_()
792
793    def setColors(self):
794        dlg = self.createColorDialog()
795        if dlg.exec_():
796            self.colorSettings = dlg.getColorSchemas()
797            self.selectedSchemaIndex = dlg.selectedSchemaIndex
798            self.colorPalette = dlg.getDiscretePalette("discPalette")
799            if self.data and self.data.domain.classVar and self.data.domain.classVar.varType == orange.VarTypes.Discrete:
800                self.colorPalette.setNumberOfColors(len(self.data.domain.classVar.values))
801            self.updateGraph()
802
803    def createColorDialog(self):
804        c = OWColorPalette.ColorPaletteDlg(self, "Color Palette")
805        c.createDiscretePalette("discPalette", "Discrete Palette", OWColorPalette.defaultRGBColors) #defaultColorBrewerPalette)
806        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex)
807        return c
808
809    # ########################################
810    # cell/example selection
811    def sendSelectedData(self):
812        # send the selected examples
813        self.send("Selected Data", self.getSelectedExamples())
814
815    # add a new rectangle. update the graph and see which mosaics does it intersect. add this mosaics to the recentlyAdded list
816    def addSelection(self, rect):
817        self.selectionRectangle = rect
818        self.updateGraph(drillUpdateSelection = 0)
819        self.sendSelectedData()
820
821        if getattr(self, "recentlyAdded", []):
822            self.selectionConditionsHistorically = self.selectionConditionsHistorically + [self.recentlyAdded]
823            self.recentlyAdded = []
824
825        self.optimizationDlg.mtUpdateState()            # we have already called this in self.updateGraph() call
826        self.selectionRectangle = None
827
828    # remove the mosaics that were added with the last selection rectangle
829    def removeLastSelection(self):
830        if self.selectionConditionsHistorically:
831            vals = self.selectionConditionsHistorically.pop()
832            for val in vals:
833                if tuple(val) in self.selectionConditions:
834                    self.selectionConditions.remove(tuple(val))
835
836        self.updateGraph()
837##        self.optimizationDlg.mtUpdateState()       # we have already called this in self.updateGraph() call
838        self.sendSelectedData()
839
840    def removeAllSelections(self):
841        self.selectionConditions = []
842        self.selectionConditionsHistorically = []
843##        self.optimizationDlg.mtUpdateState()       # removeAllSelections is always called before updateGraph() - where mtUpdateState is called
844        self.sendSelectedData()
845
846    # return examples in currently selected boxes as example table or array of 0/1 values
847    def getSelectedExamples(self, asExampleTable = 1, negate = 0, selectionConditions = None, data = None, attrs = None):
848        if attrs == None:     attrs = self.getShownAttributeList()
849        if data == None:      data = self.data
850        if selectionConditions == None:    selectionConditions = self.selectionConditions
851
852        if attrs == [] or not data:
853            return None
854
855        pp = orange.Preprocessor_take()
856        sumIndices = numpy.zeros(len(data))
857        for val in selectionConditions:
858            for i, attr in enumerate(attrs):
859                pp.values[data.domain[attr]] = val[i]
860            indices = numpy.array(pp.selectionVector(data))
861            sumIndices += indices
862        selectedIndices = list(numpy.where(sumIndices > 0, 1 - negate, 0 + negate))
863
864        if asExampleTable:
865            return data.selectref(selectedIndices)
866        else:
867            return selectedIndices
868
869    def saveSettings(self):
870        OWWidget.saveSettings(self)
871        self.optimizationDlg.saveSettings()
872
873
874
875class SortAttributeValuesDlg(OWBaseWidget):
876    def __init__(self, parentWidget = None, attr = "", valueList = []):
877        OWBaseWidget.__init__(self, None, None, "Sort Attribute Values", modal = TRUE)
878
879        self.setLayout(QVBoxLayout())
880        #self.space = QWidget(self)
881        #self.layout = QVBoxLayout(self, 4)
882        #self.layout.addWidget(self.space)
883
884        box1 = OWGUI.widgetBox(self, "Select Value Order for Attribute \"" + attr + '"', orientation = "horizontal")
885
886        self.attributeList = OWGUI.listBox(box1, self, selectionMode = QListWidget.ExtendedSelection, enableDragDrop = 1)
887        self.attributeList.addItems(valueList)
888
889        vbox = OWGUI.widgetBox(box1, "", orientation = "vertical")
890        self.buttonUPAttr   = OWGUI.button(vbox, self, "", callback = self.moveAttrUP, tooltip="Move selected attribute values up")
891        self.buttonDOWNAttr = OWGUI.button(vbox, self, "", callback = self.moveAttrDOWN, tooltip="Move selected attribute values down")
892        self.buttonUPAttr.setIcon(QIcon(os.path.join(self.widgetDir, "icons/Dlg_up3.png")))
893        self.buttonUPAttr.setSizePolicy(QSizePolicy(QSizePolicy.Fixed , QSizePolicy.Expanding))
894        self.buttonUPAttr.setFixedWidth(40)
895        self.buttonDOWNAttr.setIcon(QIcon(os.path.join(self.widgetDir, "icons/Dlg_down3.png")))
896        self.buttonDOWNAttr.setSizePolicy(QSizePolicy(QSizePolicy.Fixed , QSizePolicy.Expanding))
897        self.buttonDOWNAttr.setFixedWidth(40)
898
899        box2 = OWGUI.widgetBox(self, 1, orientation = "horizontal")
900        self.okButton =     OWGUI.button(box2, self, "OK", callback = self.accept)
901        self.cancelButton = OWGUI.button(box2, self, "Cancel", callback = self.reject)
902
903        self.resize(300, 300)
904
905    # move selected attribute values
906    def moveAttrUP(self):
907        for i in range(1, self.attributeList.count()):
908            if self.attributeList.item(i).isSelected():
909                self.attributeList.insertItem(i-1, self.attributeList.item(i).text())
910                self.attributeList.takeItem(i+1)
911                self.attributeList.item(i-1).setSelected(TRUE)
912
913    def moveAttrDOWN(self):
914        for i in range(self.attributeList.count()-2,-1,-1):
915            if self.attributeList.item(i).isSelected():
916                self.attributeList.insertItem(i+2, self.attributeList.item(i).text())
917                self.attributeList.item(i+2).setSelected(TRUE)
918                self.attributeList.takeItem(i)
919
920
921#test widget appearance
922if __name__=="__main__":
923    a=QApplication(sys.argv)
924    ow = OWMosaicDisplay()
925    ow.show()
926#    data = orange.ExampleTable(r"e:\Development\Orange Datasets\UCI\zoo.tab")
927    data = orange.ExampleTable("../../doc/datasets/zoo.tab")
928    ow.setData(data)
929    ow.handleNewSignals()
930#    for d in ["zoo.tab", "iris.tab", "zoo.tab"]:
931#        data = orange.ExampleTable(r"e:\Development\Orange Datasets\UCI\\" + d)
932#        ow.setData(data)
933#        ow.handleNewSignals()
934    a.exec_()
Note: See TracBrowser for help on using the repository browser.