source: orange/Orange/OrangeWidgets/Prototypes/OWInteractionGraphProto.py @ 10466:b52f4c49e735

Revision 10466:b52f4c49e735, 29.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Added unicode filename support for the rest of save/load widget dialogs.

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