source: orange/Orange/OrangeWidgets/Prototypes/OWInteractionGraphProto.py @ 11012:19029caa4a32

Revision 11012:19029caa4a32, 29.6 KB checked in by Miha Stajdohar <miha.stajdohar@…>, 18 months ago (diff)

Fixed more network references.

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