source: orange/orange/OrangeWidgets/Unsupervised/OWInteractionGraph.py @ 9273:9c33029c1c40

Revision 9273:9c33029c1c40, 28.2 KB checked in by ales_erjavec <ales.erjavec@…>, 2 years ago (diff)

More fixes to reporting.

Line 
1"""
2<name>Interaction Graph</name>
3<description>Interaction graph construction and viewer.</description>
4<icon>icons/InteractionGraph.png</icon>
5<contact>Aleks Jakulin</contact>
6<priority>2200</priority>
7"""
8# InteractionGraph.py
9#
10#
11from OWWidget import *
12import orngInteract, OWQCanvasFuncts
13import statc
14import os
15from re import *
16from math import floor, ceil
17from orngCI import FeatureByCartesianProduct
18import OWGUI, OWDlgs
19
20class IntGraphView(QGraphicsView):
21    def __init__(self, parent, name, *args):
22        QGraphicsView.__init__(self, *args)
23        self.parent = parent
24        self.name = name
25        self.setFocusPolicy(Qt.WheelFocus)
26
27    def sizeHint(self):
28        return QSize(200,200)
29
30    # mouse button was pressed
31    def mousePressEvent(self, ev):
32        scenePos = self.mapToScene(ev.pos())
33        pos = QPoint(int(scenePos.x()), int(scenePos.y()))
34        ev = QMouseEvent(ev.type(), pos, ev.button(), ev.buttons(), ev.modifiers())
35        self.parent.mousePressed(self.name, ev)
36       
37
38
39###########################################################################################
40##### WIDGET : Interaction graph
41###########################################################################################
42class OWInteractionGraph(OWWidget):
43    settingsList = ["onlyImportantInteractions"]
44
45    def __init__(self, parent=None, signalManager = None):
46        OWWidget.__init__(self, parent, signalManager, "Interaction graph")
47
48        self.inputs = [("Examples", ExampleTable, self.setData)]
49        self.outputs = [("Examples", ExampleTable), ("Attribute Pair", list), ("Selected Attributes List", list)]
50
51
52        #set default settings
53        self.originalData = None
54        self.data = None
55        self.dataSize = 1
56        self.rest = None
57        self.interactionMatrix = None
58        self.rectIndices = {}   # QRect rectangles
59        self.rectNames   = {}   # info about rectangle names (attributes)
60        self.lines = []         # dict of form (rectName1, rectName2):(labelQPoint, [p1QPoint, p2QPoint, ...])
61        self.interactionRects = []
62        self.rectItems = []
63
64        self.shownAttributes = []
65        self.selectedShown = []
66        self.hiddenAttributes = []
67        self.selectedHidden = []
68        self.mergeAttributes = 0
69        self.onlyImportantInteractions = 0
70
71        #load settings
72        self.loadSettings()
73
74        # add a settings dialog and initialize its values
75        self.splitter = QSplitter(Qt.Horizontal, self.mainArea)
76        self.mainArea.layout().addWidget(self.splitter)
77
78        self.canvasL = QGraphicsScene()
79        self.canvasViewL = IntGraphView(self, "interactions", self.canvasL, self)
80        self.splitter.addWidget(self.canvasViewL)
81        self.canvasViewL.setAlignment(Qt.AlignLeft | Qt.AlignTop)
82
83        self.canvasR = QGraphicsScene()
84        self.canvasViewR = IntGraphView(self, "graph", self.canvasR, self)
85        self.splitter.addWidget(self.canvasViewR)
86
87        #GUI
88        #add controls to self.controlArea widget
89        self.shownAttribsGroup = OWGUI.widgetBox(self.controlArea, "Selected Attributes")
90        self.addRemoveGroup = OWGUI.widgetBox(self.controlArea, 1, orientation = "horizontal" )
91        self.hiddenAttribsGroup = OWGUI.widgetBox(self.controlArea, "Unselected Attributes", addSpace=True)
92
93        self.shownAttribsLB = OWGUI.listBox(self.shownAttribsGroup, self, "selectedShown", "shownAttributes", selectionMode = QListWidget.ExtendedSelection)
94
95        self.attrAddButton =    OWGUI.button(self.addRemoveGroup, self, "", callback = self.addAttributeClick, tooltip="Add (show) selected attributes")
96        self.attrAddButton.setIcon(QIcon(os.path.join(self.widgetDir, "icons/Dlg_up3.png")))
97        self.attrRemoveButton = OWGUI.button(self.addRemoveGroup, self, "", callback = self.removeAttributeClick, tooltip="Remove (hide) selected attributes")
98        self.attrRemoveButton.setIcon(QIcon(os.path.join(self.widgetDir, "icons/Dlg_down3.png")))
99
100        self.hiddenAttribsLB = OWGUI.listBox(self.hiddenAttribsGroup, self, "selectedHidden", "hiddenAttributes", selectionMode = QListWidget.ExtendedSelection)
101
102        settingsBox = OWGUI.widgetBox(self.controlArea, "Settings", addSpace=True)
103        self.mergeAttributesCB = OWGUI.checkBox(settingsBox, self, "mergeAttributes", 'Merge attributes', callback = self.mergeAttributesEvent, tooltip = "Enable or disable attribute merging. If enabled, you can merge \ntwo attributes with a right mouse click inside interaction rectangles in the left graph.\nA merged attribute is then created as a cartesian product of corresponding attributes \nand added to the list of attributes.")
104        self.importantInteractionsCB = OWGUI.checkBox(settingsBox, self, "onlyImportantInteractions", 'Show only important interactions', callback = self.showImportantInteractions)
105
106        self.selectionButton = OWGUI.button(self.controlArea, self, "Show selection", callback = self.selectionClick, tooltip = "Sends 'selection' signal to any successor visualization widgets.\nThis signal contains a list of selected attributes to visualize.")
107
108        import sip
109        sip.delete(self.buttonBackground.layout())
110       
111        layout = QGridLayout()
112        layout.setContentsMargins(*self.controlArea.layout().getContentsMargins())
113#        layout.setContentsMargins(0, 0, 0, 0)
114        layout.setSpacing(4)
115       
116        self.saveLCanvas = OWGUI.button(None, self, "Save left canvas", callback = self.saveToFileLCanvas, debuggingEnabled = 0, addToLayout=False)
117        self.saveRCanvas = OWGUI.button(None, self, "Save right canvas", callback = self.saveToFileRCanvas, debuggingEnabled = 0, addToLayout=False)
118       
119#        if OWReport.report:
120#            self.reportButton = OWGUI.button(None, self, "&Report", self.reportAndFinish, debuggingEnabled=0, addToLayout=False)
121
122        layout.addWidget(self.saveLCanvas, 0, 0)
123        layout.addWidget(self.saveRCanvas, 0, 1)
124       
125        if hasattr(self, "reportButton"):
126            layout.addWidget(self.reportButton, 1, 0, 1, 2)
127        self.buttonBackground.setLayout(layout)
128        self.buttonBackground.show()
129
130    def mergeAttributesEvent(self):
131        self.updateNewData(self.originalData)
132
133    def showImportantInteractions(self):
134        self.showInteractionRects(self.data)
135
136    # did we click inside the rect rectangle
137    def clickInside(self, rect, point):
138        x = point.x()
139        y = point.y()
140
141        if rect.left() > x: return 0
142        if rect.right() < x: return 0
143        if rect.top() > y: return 0
144        if rect.bottom() < y: return 0
145
146        return 1
147
148    # if we clicked on edge label send "wiew" signal, if clicked inside rectangle select/unselect attribute
149    def mousePressed(self, name, ev):
150        pos = QPointF(ev.pos())
151        if ev.button() == Qt.LeftButton and name == "graph":
152            for name in self.rectNames:
153                if self.rectNames[name].contains(pos):
154                    self._setAttrVisible(name, not self.getAttrVisible(name))
155                    self.showInteractionRects(self.data)
156                    #self.canvasR.update()
157                    return
158            for (attr1, attr2, rect) in self.lines:
159                if rect.contains(pos):
160                    self.send("Attribute Pair", [attr1, attr2])
161                    return
162        elif ev.button() == Qt.LeftButton and name == "interactions":
163            self.rest = None
164            for rects in self.interactionRects:
165                if 1 in [item.contains(pos) for item in rects]:
166                    (rect1, rect2, rect3, nbrect, text1, text2) = rects
167                    self.send("Attribute Pair", [str(text1.toPlainText()), str(text2.toPlainText())])
168
169        elif ev.button() == Qt.RightButton and name == "interactions" and self.mergeAttributes:
170            found = 0
171            for rects in self.interactionRects:
172                (rect1, rect2, rect3, nbrect, text1, text2) = rects
173                if 1 in [item.contains(pos) for item in rects]:
174                    attr1 = str(text1.toPlainText()); attr2 = str(text2.toPlainText())
175                    found = 1
176                    break
177            if not found: return
178
179            data = self.interactionMatrix.discData
180            (cart, profit) = FeatureByCartesianProduct(data, [data.domain[attr1], data.domain[attr2]])
181            if cart in data.domain: return  # if this attribute already in domain return
182
183            for attr in data.domain:
184                if cart.name == attr.name:
185                    print "Attribute pair already in the domain"
186                    return
187
188            tempData = data.select(list(data.domain) + [cart])
189            dd = orange.DomainDistributions(tempData)
190            vals = []
191            for i in range(len(cart.values)):
192                if dd[cart][i] != 0.0:
193                    vals.append(cart.values[i])
194
195            newVar = orange.EnumVariable(cart.name, values = vals)
196            newData = data.select(list(data.domain) + [newVar])
197            for i in range(len(newData)):
198                newData[i][newVar] = tempData[i][cart]
199
200            #rest = newData.select({cart.name:todoList})
201
202            #print "intervals = %d, non clear values = %d" % (len(cart.values), len(todoList))
203            #print "entropy left = %f" % (float(len(rest)) / float(self.dataSize))
204            self.updateNewData(newData)
205
206
207    # click on selection button
208    def selectionClick(self):
209        if self.data == None: return
210        l = [str(self.shownAttribsLB.item(i).text()) for i in range(self.shownAttribsLB.count())]
211        self.send("Selected Attributes List", l)
212
213
214    # receive new data and update all fields
215    def setData(self, data):
216        self.warning([0,1])
217        self.error(0)
218
219        self.originalData = self.isDataWithClass(data, orange.VarTypes.Discrete) and data or None
220        if not self.originalData:
221            return
222
223        self.originalData = orange.Preprocessor_dropMissing(self.originalData)
224           
225        if len(self.originalData) != len(data):
226            self.warning(0, "Examples with missing values were removed. Keeping %d of %d examples." % (len(self.originalData), len(data)))
227        if len(self.originalData) == 0:
228            self.error(0, "All examples from the data set were removed!")
229            return
230        if self.originalData.domain.hasContinuousAttributes():
231            self.warning(1, "Continuous attributes were discretized using entropy discretization.")
232
233        self.dataSize = len(self.originalData)
234
235        self.updateNewData(self.originalData)
236
237    def updateNewData(self, data):
238        if not data:
239            return
240        self.data = data
241        self.interactionMatrix = orngInteract.InteractionMatrix(data, dependencies_too=1)
242
243        # save discretized data and repair invalid names
244        for attr in self.interactionMatrix.discData.domain.attributes:
245            attr.name = attr.name.replace("ED_","")
246            attr.name = attr.name.replace("D_","")
247            attr.name = attr.name.replace("M_","")
248
249        self.interactionList = []
250        entropy = self.interactionMatrix.entropy
251        if entropy == 0.0: return
252
253        ################################
254        # create a sorted list of total information
255        for ((val,(val2, attrIndex1, attrIndex2))) in self.interactionMatrix.list:
256            gain1 = self.interactionMatrix.gains[attrIndex1] / entropy
257            gain2 = self.interactionMatrix.gains[attrIndex2] / entropy
258            total = (val/entropy) + gain1 + gain2
259            self.interactionList.append((total, (gain1, gain2, attrIndex1, attrIndex2)))
260        self.interactionList.sort()
261        self.interactionList.reverse()
262
263        import tempfile, os
264        fhandle, fname = tempfile.mkstemp('.dot')
265        os.close(fhandle)
266        f = file(fname, "wt")
267        self.interactionMatrix.exportGraph(f, significant_digits=3,positive_int=8,negative_int=8,absolute_int=0,url=1)
268        f.flush()
269        f.close()
270        del f
271
272        import subprocess
273        def run_dot(dotfile, format):
274            # execute dot command to retrieve an image in 'format'
275            # and an ismap formated dot output.
276            # Using temp files instead of direct pipe output due to EINTR
277            # errors
278            image_handle, image_name = tempfile.mkstemp('.' + format)
279            os.close(image_handle)
280           
281            ismap_handle, ismap_name = tempfile.mkstemp('.ismap')
282            os.close(ismap_handle)
283           
284#            print image_name
285#            subprocess.check_call(" ".join(["dot", fname, "-T" + format, "-o" + image_name]), shell=True)
286#            subprocess.check_call(" ".join(["dot", fname, "-Tismap", "-o" + ismap_name]), shell=True)
287            subprocess.check_call(["dot", fname, "-T" + format, "-o" + image_name])
288            subprocess.check_call(["dot", fname, "-Tismap", "-o" + ismap_name])
289
290           
291            image_data = open(image_name, "rb").read()
292            ismap_data = open(ismap_name, "rb").read()
293           
294            os.remove(image_name)
295            os.remove(ismap_name)
296           
297            return image_data, ismap_data
298
299        self.error([0, 1])
300        ismap = ""
301        image = ""
302        pixmap = QPixmap()
303        try:
304            # testing different formats, sometimes the png output from 'dot'
305            # fails to load in Qt (no idea why).
306            for format in ["png", "gif", "jpeg", "svg"]: 
307                image, ismap = run_dot(fname, format)
308                qimage = QImage.fromData(QByteArray(image), format)
309                pixmap = QPixmap.fromImage(qimage)
310                if not pixmap.isNull():
311                    break
312        except OSError, ex:
313            self.error(0, "This widget needs 'graphviz' software package. You can freely download it from the web.")
314        except subprocess.CalledProcessError, ex:
315            self.error(0, "This widget needs 'graphviz' software package. You can freely download it from the web.")
316           
317        if image and pixmap.isNull():
318            self.error(1, "Could not load image from 'dot' output.")
319       
320        textPlainList = ismap.splitlines()
321       
322        os.remove(fname)
323       
324        if hasattr(self, "canvasPixmap"):
325            self.canvasPixmap.setPixmap(pixmap)
326        else:
327            self.canvasPixmap = self.canvasR.addPixmap(pixmap)
328           
329        width = self.canvasPixmap.pixmap().width()
330        height = self.canvasPixmap.pixmap().height()
331       
332        self.canvasR.setSceneRect(QRectF(0, 0, width, height).adjusted(-10, -10, 10, 10))
333        #self.canvasR.resize(width, height)
334
335        # hide all rects
336        for rects in self.rectIndices.values():
337            rects.hide()
338
339        self.rectIndices = {}       # QRect rectangles
340        self.rectNames   = {}       # info about rectangle names (attributes)
341        self.lines = []             # dict of form (rectName1, rectName2):(labelQPoint, [p1QPoint, p2QPoint, ...])
342
343        self.parseGraphData(data, textPlainList, width, height)
344        self.initLists(data)   # add all attributes found in .dot file to shown list
345        self.showInteractionRects(data) # use interaction matrix to fill the left canvas with rectangles
346
347        #self.canvasL.update()
348        #self.canvasR.update()
349
350        self.send("Examples", data)
351
352
353    #########################################
354    # do we want to show interactions between attrIndex1 and attrIndex2
355    def showInteractionPair(self, attrIndex1, attrIndex2):
356        attrName1 = self.data.domain[attrIndex1].name
357        attrName2 = self.data.domain[attrIndex2].name
358
359        if self.mergeAttributes == 1:
360            if self.getAttrVisible(attrName1) == 0 or self.getAttrVisible(attrName2) == 0: return 0
361            list1 = attrName1.split("-")
362            list2 = attrName2.split("-")
363            for item in list1:
364                if item in list2: return 0
365            for item in list2:
366                if item in list1: return 0
367            #return 1
368
369        if self.getAttrVisible(attrName1) == 0 or self.getAttrVisible(attrName2) == 0: return 0
370        if self.onlyImportantInteractions == 1:
371            for (attr1, attr2, rect) in self.lines:
372                if (attr1 == attrName1 and attr2 == attrName2) or (attr1 == attrName2 and attr2 == attrName1): return 1
373            return 0
374        return 1
375
376    #########################################
377    # show interactions between attributes in left canvas
378    def showInteractionRects(self, data):
379        if self.interactionMatrix == None: return
380        if self.data == None : return
381
382        ################################
383        # hide all interaction rectangles
384        for (rect1, rect2, rect3, nbrect, text1, text2) in self.interactionRects:
385            for item in [rect1, rect2, rect3, nbrect, text1, text2]:
386                self.canvasL.removeItem(item)
387        self.interactionRects = []
388
389        for item in self.rectItems:
390            self.canvasL.removeItem(item)
391        self.rectItems = []
392
393        ################################
394        # get max width of the attribute text
395        xOff = 0
396        for ((total, (gain1, gain2, attrIndex1, attrIndex2))) in self.interactionList:
397            if not self.showInteractionPair(attrIndex1, attrIndex2): continue
398            if gain1 > gain2: text = OWQCanvasFuncts.OWCanvasText(self.canvasL, data.domain[attrIndex1].name, show = 0)
399            else:             text = OWQCanvasFuncts.OWCanvasText(self.canvasL, data.domain[attrIndex2].name, show = 0)
400            xOff = max(xOff, text.boundingRect().width())
401
402        xOff += 10;  yOff = 40
403        index = 0
404        xscale = 300;  yscale = 200
405        maxWidth = xOff + xscale + 10;  maxHeight = 0
406        rectHeight = yscale * 0.1    # height of the rectangle will be 1/10 of max width
407
408        ################################
409        # print scale
410        line = OWQCanvasFuncts.OWCanvasLine(self.canvasL, xOff, yOff - 4, xOff+xscale, yOff-4)
411        tick1 = OWQCanvasFuncts.OWCanvasLine(self.canvasL, xOff, yOff-10, xOff, yOff-4)
412        tick2 = OWQCanvasFuncts.OWCanvasLine(self.canvasL, xOff + (xscale/2), yOff-10, xOff + (xscale/2), yOff-4)
413        tick3 = OWQCanvasFuncts.OWCanvasLine(self.canvasL, xOff + xscale-1, yOff-10, xOff + xscale-1, yOff-4)
414        self.rectItems = [line, tick1, tick2, tick3]
415        for i in range(10):
416            x = xOff + xscale * (float(i)/10.0)
417            tick = OWQCanvasFuncts.OWCanvasLine(self.canvasL, x, yOff-8, x, yOff-4)
418            self.rectItems.append(tick)
419
420        text1 = OWQCanvasFuncts.OWCanvasText(self.canvasL, "0%", x = xOff, y = yOff - 23, alignment = Qt.AlignHCenter)
421        text2 = OWQCanvasFuncts.OWCanvasText(self.canvasL, "50%", x = xOff + xscale/2, y = yOff - 23, alignment = Qt.AlignHCenter)
422        text3 = OWQCanvasFuncts.OWCanvasText(self.canvasL, "100%", x = xOff + xscale, y = yOff - 23, alignment = Qt.AlignHCenter)
423        text4 = OWQCanvasFuncts.OWCanvasText(self.canvasL, "Class entropy removed", x = xOff + xscale/2, y = yOff - 36, alignment = Qt.AlignHCenter)
424        self.rectItems += [text1, text2, text3, text4]
425
426        ################################
427        #create rectangles
428        for ((total, (gain1, gain2, attrIndex1, attrIndex2))) in self.interactionList:
429            if not self.showInteractionPair(attrIndex1, attrIndex2): continue
430
431            interaction = (total - gain1 - gain2)
432            atts = (max(attrIndex1, attrIndex2), min(attrIndex1, attrIndex2))
433            #nbgain = self.interactionMatrix.ig[atts[0]][atts[1]] + self.interactionMatrix.gains[atts[0]] + self.interactionMatrix.gains[atts[1]]
434            nbgain = self.interactionMatrix.gains[atts[0]] + self.interactionMatrix.gains[atts[1]]
435            nbgain -= self.interactionMatrix.corr[(atts[1],atts[0])]
436            rectsYOff = yOff + 3 + index * yscale * 0.15
437
438            # swap if gain1 < gain2
439            if gain1 < gain2:
440                ind = attrIndex1; attrIndex1 = attrIndex2; attrIndex2 = ind
441                ga = gain1; gain1 = gain2;  gain2 = ga
442
443            x1 = round(xOff)
444            if interaction < 0:
445                x2 = xOff + xscale*(gain1+interaction)
446                x3 = xOff + xscale*gain1
447            else:
448                x2 = xOff + xscale*gain1
449                x3 = xOff + xscale*(total-gain2)
450            x4 = xOff + xscale*total
451
452            # compute nbgain position
453            nb_x1 = min(xOff, xOff + 0.5*xscale*nbgain)
454            nb_x2 = max(xOff, xOff + 0.5*xscale*nbgain)
455
456            tooltipText = "%s : <b>%.1f%%</b><br>%s : <b>%.1f%%</b><br>Interaction : <b>%.1f%%</b><br>Total entropy removed: <b>%.1f%%</b>" %(data.domain[attrIndex1].name, gain1*100, data.domain[attrIndex2].name, gain2*100, interaction*100, total*100)
457
458            nbrect = OWQCanvasFuncts.OWCanvasRectangle(self.canvasL, nb_x1, rectsYOff-4, nb_x2-nb_x1+1, 2, brushColor = Qt.black)
459            rect2 = OWQCanvasFuncts.OWCanvasRectangle(self.canvasL, x2, rectsYOff,   x3-x2+1, rectHeight, tooltip = tooltipText)
460            rect1 = OWQCanvasFuncts.OWCanvasRectangle(self.canvasL, x1, rectsYOff, x2-x1+1, rectHeight, tooltip = tooltipText)
461            rect3 = OWQCanvasFuncts.OWCanvasRectangle(self.canvasL, x3, rectsYOff, x4-x3, rectHeight, tooltip = tooltipText)
462
463            if interaction < 0.0:
464                color = QColor(200, 0, 0)
465                style = Qt.DiagCrossPattern
466            else:
467                color = QColor(Qt.green)
468                style = Qt.Dense5Pattern
469
470            brush1 = QBrush(Qt.blue); brush1.setStyle(Qt.BDiagPattern)
471            brush2 = QBrush(color);   brush2.setStyle(style)
472            brush3 = QBrush(Qt.blue); brush3.setStyle(Qt.FDiagPattern)
473
474            rect1.setBrush(brush1); rect1.setPen(QPen(QColor(Qt.blue)))
475            rect2.setBrush(brush2); rect2.setPen(QPen(color))
476            rect3.setBrush(brush3); rect3.setPen(QPen(QColor(Qt.blue)))
477
478            # create text labels
479            text1 = OWQCanvasFuncts.OWCanvasText(self.canvasL, data.domain[attrIndex1].name, x = xOff - 5, y = rectsYOff + 3, alignment = Qt.AlignRight)
480            text2 = OWQCanvasFuncts.OWCanvasText(self.canvasL, data.domain[attrIndex2].name, x = xOff + xscale*total + 5, y = rectsYOff + 3, alignment = Qt.AlignLeft)
481
482            # compute line width
483            rect = text2.boundingRect()
484            lineWidth = xOff + xscale*total + 5 + rect.width() + 10
485            if  lineWidth > maxWidth:
486                maxWidth = lineWidth
487
488            if rectsYOff + rectHeight + 10 > maxHeight:
489                maxHeight = rectsYOff + rectHeight + 10
490
491            self.interactionRects.append((rect1, rect2, rect3, nbrect, text1, text2))
492            index += 1
493
494        # resizing of the left canvas to update width
495        #self.canvasViewL.setMaximumSize(QSize(maxWidth + 30, max(2000, maxHeight)))
496        #self.canvasL.resize(maxWidth + 10, maxHeight)
497        #self.canvasViewL.setMinimumWidth(0)
498        #self.canvasL.update()
499
500
501    #########################################
502    # parse info from plain file. picWidth and picHeight are sizes in pixels
503    def parseGraphData(self, data, textPlainList, picWidth, picHeight):
504        scale = 0
505        w = 1; h = 1
506        for line in textPlainList:
507            if line[:9] == "rectangle":
508                list = line.split()
509                topLeftRectStr = list[1]
510                bottomRightRectStr = list[2]
511                attrIndex = list[3]
512                isAttribute = "-" not in attrIndex     # does rectangle represent attribute
513
514                topLeftRectStr = topLeftRectStr.replace("(","")
515                bottomRightRectStr = bottomRightRectStr.replace("(","")
516                topLeftRectStr = topLeftRectStr.replace(")","")
517                bottomRightRectStr = bottomRightRectStr.replace(")","")
518
519                topLeftRectList = topLeftRectStr.split(",")
520                bottomRightRectList = bottomRightRectStr.split(",")
521                xLeft = int(topLeftRectList[0])
522                yTop = int(topLeftRectList[1])
523                width = int(bottomRightRectList[0]) - xLeft
524                height = int(bottomRightRectList[1]) - yTop
525
526                rect = OWQCanvasFuncts.OWCanvasRectangle(self.canvasR, xLeft, yTop, width, height, penColor = Qt.blue, penWidth = 4, show = 0)
527
528                if isAttribute == 1:
529                    name = data.domain[int(attrIndex)].name
530                    self.rectIndices[int(attrIndex)] = rect
531                    self.rectNames[name] = rect
532                else:
533                    attrs = attrIndex.split("-")
534                    attr1 = data.domain[int(attrs[0])].name
535                    attr2 = data.domain[int(attrs[1])].name
536                    rect.setPen(QPen(Qt.NoPen))
537                    self.lines.append((attr1, attr2, rect))
538
539    ##################################################
540    # initialize lists for shown and hidden attributes
541    def initLists(self, data):
542        self.shownAttribsLB.clear()
543        self.hiddenAttribsLB.clear()
544
545        if data == None: return
546
547        for key in self.rectNames.keys():
548            self._setAttrVisible(key, 1)
549
550
551    #################################################
552    ### showing and hiding attributes
553    #################################################
554    def _showAttribute(self, name):
555        self.shownAttribsLB.addItem(name)    # add to shown
556
557        count = self.hiddenAttribsLB.count()
558        for i in range(count-1, -1, -1):        # remove from hidden
559            if str(self.hiddenAttribsLB.item(i).text()) == name:
560                self.hiddenAttribsLB.takeItem(i)
561
562    def _hideAttribute(self, name):
563        self.hiddenAttribsLB.addItem(name)    # add to hidden
564
565        count = self.shownAttribsLB.count()
566        for i in range(count-1, -1, -1):        # remove from shown
567            if str(self.shownAttribsLB.item(i).text()) == name:
568                self.shownAttribsLB.takeItem(i)
569
570    ##########
571    # add attribute to showList or hideList and show or hide its rectangle
572    def _setAttrVisible(self, name, visible = 1):
573        if visible == 1:
574            if name in self.rectNames.keys(): self.rectNames[name].show();
575            self._showAttribute(name)
576        else:
577            if name in self.rectNames.keys(): self.rectNames[name].hide();
578            self._hideAttribute(name)
579
580    def getAttrVisible(self, name):
581        for i in range(self.hiddenAttribsLB.count()):
582            if str(self.hiddenAttribsLB.item(i).text()) == name: return 0
583
584        if self.mergeAttributes == 1:
585            names = name.split("-")
586            for i in range(self.hiddenAttribsLB.count()):
587                if str(self.hiddenAttribsLB.item(i).text()) in names: return 0
588
589        return 1
590
591    #################################################
592    # event processing
593    #################################################
594    def addAttributeClick(self):
595        count = self.hiddenAttribsLB.count()
596        for i in range(count-1, -1, -1):
597            if self.hiddenAttribsLB.item(i).isSelected():
598                name = str(self.hiddenAttribsLB.item(i).text())
599                self._setAttrVisible(name, 1)
600        self.showInteractionRects(self.data)
601        self.canvasL.update()
602        self.canvasR.update()
603
604    def removeAttributeClick(self):
605        count = self.shownAttribsLB.count()
606        for i in range(count-1, -1, -1):
607            if self.shownAttribsLB.item(i).isSelected():
608                name = str(self.shownAttribsLB.item(i).text())
609                self._setAttrVisible(name, 0)
610        self.showInteractionRects(self.data)
611        self.canvasL.update()
612        self.canvasR.update()
613
614    ##################################################
615    # SAVING GRAPHS
616    ##################################################
617    def saveToFileLCanvas(self):
618        sizeDlg = OWDlgs.OWChooseImageSizeDlg(self.canvasL, parent=self)
619        sizeDlg.exec_()
620
621
622    def saveToFileRCanvas(self):
623        sizeDlg = OWDlgs.OWChooseImageSizeDlg(self.canvasR, parent=self)
624        sizeDlg.exec_()
625
626    def sendReport(self):
627        self.reportData(self.data)
628        self.reportSettings("", [("Selected attributes", ", ".join(str(self.shownAttribsLB.item(i).text()) for i in range(self.shownAttribsLB.count())))])
629        if self.data:
630            self.reportSection("Pairwise interaction bars")
631            self.reportImage(OWDlgs.OWChooseImageSizeDlg(self.canvasL).saveImage)
632            self.reportSection("Interaction graph")
633            self.reportImage(OWDlgs.OWChooseImageSizeDlg(self.canvasR).saveImage)
634                           
635
636#test widget appearance
637if __name__=="__main__":
638    a=QApplication(sys.argv)
639    ow=OWInteractionGraph()
640    ow.show()
641    ow.setData(data = orange.ExampleTable("../../doc/datasets/monks-1_learn.tab"))
642    a.exec_()
643
644    #save settings
645    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.