source: orange/Orange/OrangeWidgets/OWTreeViewer2D.py @ 11038:51e701fc9845

Revision 11038:51e701fc9845, 26.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 17 months ago (diff)

Change the node text color, if the background is to dark.

Fixes #1240

Line 
1import orange, orngTree, OWGUI, OWColorPalette
2from OWWidget import *
3
4import Orange
5
6from PyQt4.QtCore import *
7from PyQt4.QtGui import *
8
9DefDroppletRadiust=7
10DefNodeWidth=30
11DefNodeHeight=20
12DefDroppletBrush=QBrush(Qt.darkGray)
13
14class graph_node(object):
15    def __init__(self, *args, **kwargs):
16        self.edges = kwargs.get("edges", set())
17       
18    def graph_edges(self):
19        return self.edges
20   
21    def graph_add_edge(self, edge):
22        self.edges.add(edge)
23       
24    def __iter__(self):
25        for edge in self.edges:
26            yield edge.node2
27           
28    def graph_nodes(self, type=1):
29        pass
30           
31class graph_edge(object):
32       
33    def __init__(self, node1=None, node2=None, type=1):
34        self.node1 = node1
35        self.node2 = node2
36        self.type = type
37        node1.graph_add_edge(self)
38        node2.graph_add_edge(self)
39       
40class GraphicsDroplet(QGraphicsEllipseItem):
41   
42    def __init__(self, *args):
43        QGraphicsEllipseItem.__init__(self, *args)
44        self.setAcceptHoverEvents(True)
45        self.setAcceptedMouseButtons(Qt.LeftButton)
46        self.setBrush(QBrush(Qt.gray))
47       
48    def hoverEnterEvent(self, event):
49        QGraphicsEllipseItem.hoverEnterEvent(self, event)
50        self.setBrush(QBrush(QColor(100, 100, 100)))
51        self.update()
52       
53    def hoverLeaveEvent(self, event):
54        QGraphicsEllipseItem.hoverLeaveEvent(self, event)
55        self.setBrush(QBrush(QColor(200, 200, 200)))
56        self.update()
57       
58    def mousePressEvent(self, event):
59        QGraphicsEllipseItem.mousePressEvent(self, event)
60        self.parentItem().setOpen(not self.parentItem().isOpen)
61        if self.scene():
62            self.scene().fixPos()
63
64
65def luminance(color):
66    """Return the `luminance`_ (sRGB color space) of the color.
67
68    .. _luminance: http://en.wikipedia.org/wiki/Luminance_(colorimetry)
69
70    """
71    r, g, b, _ = color.getRgb()
72    Y = 0.2126 * r + 0.7152 * g + 0.0722 * b
73    return Y
74
75
76class TextTreeNode(QGraphicsTextItem, graph_node):
77    """A Tree node with text.
78    """
79    def setBorderRadius(self, r):
80        if self._borderRadius != r:
81            self.prepareGeometryChange()
82            self._borderRadius = r
83            self.update()
84
85    def borderRadius(self):
86        return getattr(self, "_borderRadius", 0)
87
88    borderRadius = pyqtProperty("int", fget=borderRadius, fset=setBorderRadius,
89                                doc="Rounded rect's border radius")
90
91    def setBackgroundBrush(self, brush):
92        """Set node's background brush.
93        """
94        if self._backgroundBrush != brush:
95            self._backgroundBrush = QBrush(brush)
96            color = brush.color()
97            if luminance(color) > 30:
98                self.setDefaultTextColor(Qt.black)
99            else:
100                self.setDefaultTextColor(Qt.white)
101            self.update()
102
103    def backgroundBrush(self):
104        """Return the node's background brush.
105        """
106        brush = getattr(self, "_backgroundBrush",
107                        getattr(self.scene(), "defaultItemBrush", Qt.NoBrush))
108        return QBrush(brush)
109
110    backgroundBrush = pyqtProperty("QBrush", fget=backgroundBrush,
111                                   fset=setBackgroundBrush,
112                                   doc="Background brush")
113
114    def setTruncateText(self, truncate):
115        """Set the truncateText to truncate. If true the text will
116        be truncated to fit inside the node's box, otherwise it will
117        overflow.
118
119        """
120        if self._truncateText != truncate:
121            self._truncateText = truncate
122            self.updateContents()
123
124    def truncateText(self):
125        return getattr(self, "_truncateText", False)
126
127    truncateText = pyqtProperty("bool", fget=truncateText,
128                                fset=setTruncateText,
129                                doc="Truncate text")
130
131    def __init__(self, tree, parent, *args, **kwargs):
132        QGraphicsTextItem.__init__(self, *args)
133        graph_node.__init__(self, **kwargs)
134        self._borderRadius = 0
135        self._backgroundBrush = None
136        self._truncateText = False
137
138        self.tree = tree
139        self.parent = parent
140        font = self.font()
141        font.setPointSize(10)
142        self.setFont(font)
143        self.droplet = GraphicsDroplet(-5, 0, 10, 10, self, self.scene())
144
145        self.droplet.setPos(self.rect().center().x(), self.rect().height())
146
147        self.connect(self.document(), SIGNAL("contentsChanged()"),
148                     self.updateContents)
149        self.isOpen = True
150        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
151
152    def setHtml(self, html):
153        if qVersion() < "4.5":
154            html = html.replace("<hr>", "<hr width=200>") #bug in Qt4.4 (need width = 200)
155        return QGraphicsTextItem.setHtml(self, "<body>" + html + "</body>") 
156   
157    def updateContents(self):
158        if getattr(self, "_rect", QRectF()).isValid() and not self.truncateText:
159            self.setTextWidth(self._rect.width())
160        else:
161            self.setTextWidth(-1)
162            self.setTextWidth(self.document().idealWidth())
163        self.droplet.setPos(self.rect().center().x(), self.rect().height())
164        self.droplet.setVisible(bool(self.branches))
165       
166    def setRect(self, rect):
167        self.prepareGeometryChange()
168        rect = QRectF() if rect is None else rect
169        self._rect = rect
170        self.updateContents()
171        self.update()
172       
173    def shape(self):
174        path = QPainterPath()
175        path.addRect(self.boundingRect())
176        return path
177   
178    def rect(self):
179        if self.truncateText and getattr(self, "_rect", QRectF()).isValid():
180            return self._rect
181        else:
182            return QRectF(QPointF(0,0), self.document().size()) | getattr(self, "_rect", QRectF(0, 0, 1, 1))
183       
184    def boundingRect(self):
185        if self.truncateText and getattr(self, "_rect", QRectF()).isValid():
186            return self._rect
187        else:
188            return QGraphicsTextItem.boundingRect(self)
189   
190    @property 
191    def branches(self):
192        return [edge.node2 for edge in self.graph_edges() if edge.node1 is self]
193   
194    def paint(self, painter, option, widget=0):
195        painter.save()
196        painter.setBrush(self.backgroundBrush)
197        rect = self.rect()
198        painter.drawRoundedRect(rect, self.borderRadius, self.borderRadius)
199        painter.restore()
200        painter.setClipRect(rect)
201        return QGraphicsTextItem.paint(self, painter, option, widget)
202       
203def graph_traverse_bf(nodes, level=None, test=None):
204    visited = set()
205    queue = list(nodes)
206    while queue:
207        node = queue.pop(0)
208        if node not in visited:
209            yield node
210            visited.add(node)
211            if not test or test(node):
212                queue.extend(list(node))
213               
214class GraphicsNode(TextTreeNode):
215    def setOpen(self, open, level=1):
216        self.isOpen = open
217        for node in graph_traverse_bf(self, test=lambda node: node.isOpen):
218            if node is not self:
219                node.setVisible(open)
220               
221    def itemChange(self, change, value):
222        if change == QGraphicsItem.ItemPositionHasChanged:
223            self.updateEdge()
224        elif change == QGraphicsItem.ItemVisibleHasChanged:
225            self.updateEdge()
226           
227        return TextTreeNode.itemChange(self, change, value)
228   
229    def updateEdge(self):
230        for edge in self.edges:
231            if edge.node1 is self:
232                QTimer.singleShot(0, edge.updateEnds)
233            elif edge.node2 is self:
234                edge.setVisible(self.isVisible())
235               
236    def edgeInPoint(self, edge):
237        return edge.mapFromItem(self, QPointF(self.rect().center().x(), self.rect().y()))
238
239    def edgeOutPoint(self, edge):
240        return edge.mapFromItem(self.droplet, self.droplet.rect().center())
241   
242    def paint(self, painter, option, widget=0):
243        if self.isSelected():
244            option.state = option.state.__xor__(QStyle.State_Selected)
245        if self.isSelected():
246            rect = self.rect()
247            painter.save()
248#            painter.setBrush(QBrush(QColor(100, 0, 255, 100)))
249            painter.setBrush(QBrush(QColor(125, 162, 206, 192)))
250            painter.drawRoundedRect(rect.adjusted(-4, -4, 4, 4), self.borderRadius, self.borderRadius)
251            painter.restore()
252        TextTreeNode.paint(self, painter, option, widget)
253       
254    def boundingRect(self):
255        return TextTreeNode.boundingRect(self).adjusted(-5, -5, 5, 5)
256           
257    def mousePressEvent(self, event):
258        return TextTreeNode.mousePressEvent(self, event)
259   
260class GraphicsEdge(QGraphicsLineItem, graph_edge):
261    def __init__(self, *args, **kwargs):
262        QGraphicsLineItem.__init__(self, *args)
263        graph_edge.__init__(self, **kwargs)
264        self.setZValue(-30)
265       
266    def updateEnds(self):
267        try:
268            self.prepareGeometryChange()
269            self.setLine(QLineF(self.node1.edgeOutPoint(self), self.node2.edgeInPoint(self)))
270        except RuntimeError: # this gets called through QTimer.singleShot and might already be deleted by Qt
271            pass 
272
273class TreeGraphicsView(QGraphicsView):
274    def __init__(self, master, scene, *args):
275        QGraphicsView.__init__(self, scene, *args)
276#        try:
277#            import PyQt4.QtOpenGL as gl
278#            fmt = gl.QGLFormat()
279#            fmt.setSampleBuffers(True)
280#            fmt.setSamples(32)
281#            print fmt.sampleBuffers()
282#            self.setViewport(gl.QGLWidget(fmt, self))
283#        except Exception, ex:
284#            print ex
285        self.viewport().setMouseTracking(True)
286        self.setFocusPolicy(Qt.WheelFocus)
287        self.setRenderHint(QPainter.Antialiasing)
288        self.setRenderHint(QPainter.TextAntialiasing)
289        self.setRenderHint(QPainter.HighQualityAntialiasing)
290
291    def resizeEvent(self, event):
292        QGraphicsView.resizeEvent(self, event)
293        self.emit(SIGNAL("resized(QSize)"), self.size())
294
295class TreeGraphicsScene(QGraphicsScene):
296    def __init__(self, master, *args):
297        QGraphicsScene.__init__(self, *args)
298        self.HSpacing=10
299        self.VSpacing=10
300        self.master=master
301        self.nodeList=[]
302        self.edgeList=[]
303
304    def fixPos(self, node=None, x=10, y=10):
305        self.gx=x
306        self.gy=y
307        if not node:
308            if self.nodes():
309                node = [node for node in self.nodes() if not node.parent][0]
310            else:
311                return
312        if not x or not y: x, y= self.HSpacing, self.VSpacing
313        self._fixPos(node,x,y)
314       
315        self.setSceneRect(reduce(QRectF.united, [item.sceneBoundingRect() for item in self.items() if item.isVisible()], QRectF(0, 0, 10, 10)).adjusted(0, 0, 100, 100))
316#        print self.sceneRect()
317        self.update()
318       
319    def _fixPos(self, node, x, y):
320        ox=x
321       
322        def bRect(node):
323            return node.boundingRect() | node.childrenBoundingRect()
324        if node.branches and node.isOpen:
325            for n in node.branches:
326                (x,ry)=self._fixPos(n,x,y+self.VSpacing + bRect(node).height())
327            x=(node.branches[0].pos().x() + node.branches[-1].pos().x())/2
328#            print x,y
329            node.setPos(x,y)
330            for e in node.edges:
331                e.updateEnds()
332        else:
333#            print self.gx, y
334            node.setPos(self.gx,y)
335            self.gx+=self.HSpacing + bRect(node).width()
336            x+=self.HSpacing + bRect(node).width()
337            self.gy=max([y,self.gy])
338
339        return (x,y)
340
341    def mouseMoveEvent(self,event):
342        return QGraphicsScene.mouseMoveEvent(self, event)
343
344    def mousePressEvent(self, event):
345        return QGraphicsScene.mousePressEvent(self, event)
346   
347    def edges(self):
348        return [item for item in self.items() if isinstance(item, graph_edge)]
349   
350    def nodes(self):
351        return [item for item in self.items() if isinstance(item, graph_node)]     
352
353class TreeNavigator(QGraphicsView):
354
355    def __init__(self, masterView, *args):
356        QGraphicsView.__init__(self)
357        self.masterView = masterView
358        self.setScene(self.masterView.scene())
359        self.connect(self.scene(), SIGNAL("sceneRectChanged(QRectF)"), self.updateSceneRect)
360        self.connect(self.masterView, SIGNAL("resized(QSize)"), self.updateView)
361        self.setRenderHint(QPainter.Antialiasing)
362
363    def mousePressEvent(self, event):
364        if event.buttons() & Qt.LeftButton:
365            self.masterView.centerOn(self.mapToScene(event.pos()))
366            self.updateView()
367        return QGraphicsView.mousePressEvent(self, event)
368
369    def mouseMoveEvent(self, event):
370        if event.buttons() & Qt.LeftButton:
371            self.masterView.centerOn(self.mapToScene(event.pos()))
372            self.updateView()
373        return QGraphicsView.mouseMoveEvent(self, event)
374
375    def resizeEvent(self, event):
376        QGraphicsView.resizeEvent(self, event)
377        self.updateView()
378#
379    def resizeView(self):
380        self.updateView()
381
382    def updateSceneRect(self, rect):
383        QGraphicsView.updateSceneRect(self, rect)
384        self.updateView()
385       
386    def updateView(self, *args):
387        if self.scene():
388            self.fitInView(self.scene().sceneRect())
389
390    def paintEvent(self, event):
391        QGraphicsView.paintEvent(self, event)
392        painter = QPainter(self.viewport())
393        painter.setBrush(QColor(100, 100, 100, 100))
394        painter.setRenderHints(self.renderHints())
395        painter.drawPolygon(self.viewPolygon())
396       
397    def viewPolygon(self):
398        return self.mapFromScene(self.masterView.mapToScene(self.masterView.viewport().rect()))
399
400
401class OWTreeViewer2D(OWWidget):
402    settingsList = ["ZoomAutoRefresh", "AutoArrange", "ToolTipsEnabled",
403                    "Zoom", "VSpacing", "HSpacing", "MaxTreeDepth", "MaxTreeDepthB",
404                    "LineWidth", "LineWidthMethod",
405                    "MaxNodeWidth", "LimitNodeWidth", "NodeInfo", "NodeColorMethod",
406                    "TruncateText"]
407
408    def __init__(self, parent=None, signalManager = None, name='TreeViewer2D'):
409        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
410        self.root = None
411        self.selectedNode = None
412
413        self.inputs = [("Classification Tree", Orange.classification.tree.TreeClassifier, self.ctree)]
414        self.outputs = [("Examples", ExampleTable)]
415
416        #set default settings
417        self.ZoomAutoRefresh = 0
418        self.AutoArrange = 0
419        self.ToolTipsEnabled = 1
420        self.MaxTreeDepth = 5; self.MaxTreeDepthB = 0
421        self.LineWidth = 5; self.LineWidthMethod = 2
422        self.NodeSize = 5
423        self.MaxNodeWidth = 150
424        self.LimitNodeWidth = True
425        self.NodeInfo = [0, 1]
426
427        self.Zoom = 5
428        self.VSpacing = 5; self.HSpacing = 5
429        self.TruncateText = 1
430       
431        self.loadSettings()
432        self.NodeInfo.sort()
433
434# Changed when the GUI was simplified - added here to override any saved settings
435        self.VSpacing = 1; self.HSpacing = 1
436        self.ToolTipsEnabled = 1
437        self.LineWidth = 15  # Also reset when the LineWidthMethod is changed!
438       
439        # GUI definition
440#        self.tabs = OWGUI.tabWidget(self.controlArea)
441
442        # GENERAL TAB
443        # GeneralTab = OWGUI.createTabPage(self.tabs, "General")
444#        GeneralTab = TreeTab = OWGUI.createTabPage(self.tabs, "Tree")
445#        NodeTab = OWGUI.createTabPage(self.tabs, "Node")
446
447        GeneralTab = NodeTab = TreeTab = self.controlArea
448       
449        self.infBox = OWGUI.widgetBox(GeneralTab, 'Info', sizePolicy = QSizePolicy(QSizePolicy.Minimum , QSizePolicy.Fixed ), addSpace=True)
450        self.infoa = OWGUI.widgetLabel(self.infBox, 'No tree.')
451        self.infob = OWGUI.widgetLabel(self.infBox, " ")
452
453        self.sizebox = OWGUI.widgetBox(GeneralTab, "Size", addSpace=True)
454        OWGUI.hSlider(self.sizebox, self, 'Zoom', label='Zoom', minValue=1, maxValue=10, step=1,
455                      callback=self.toggleZoomSlider, ticks=1)
456        OWGUI.separator(self.sizebox)
457       
458        cb, sb = OWGUI.checkWithSpin(self.sizebox, self, "Max node width:", 50, 200, "LimitNodeWidth", "MaxNodeWidth",
459                                     tooltip="Limit the width of tree nodes",
460                                     checkCallback=self.toggleNodeSize,
461                                     spinCallback=self.toggleNodeSize,
462                                     step=10)
463        b = OWGUI.checkBox(OWGUI.indentedBox(self.sizebox, sep=OWGUI.checkButtonOffsetHint(cb)), self, "TruncateText", "Truncate text", callback=self.toggleTruncateText)
464        cb.disables.append(b)
465        cb.makeConsistent() 
466
467        OWGUI.checkWithSpin(self.sizebox, self, 'Max tree depth:', 1, 20, 'MaxTreeDepthB', "MaxTreeDepth",
468                            tooltip='Defines the depth of the tree displayed',
469                            checkCallback=self.toggleTreeDepth,
470                            spinCallback=self.toggleTreeDepth)
471       
472       
473        self.edgebox = OWGUI.widgetBox(GeneralTab, "Edge Widths", addSpace=True)
474        OWGUI.comboBox(self.edgebox, self,  'LineWidthMethod',
475                                items=['Equal width', 'Root node', 'Parent node'],
476                                callback=self.toggleLineWidth)
477        # Node information
478        grid = QGridLayout()
479        grid.setContentsMargins(*self.controlArea.layout().getContentsMargins())
480       
481        navButton = OWGUI.button(self.controlArea, self, "Navigator", self.toggleNavigator, debuggingEnabled = 0, addToLayout=False)
482#        findbox = OWGUI.widgetBox(self.controlArea, orientation = "horizontal")
483        self.centerRootButton=OWGUI.button(self.controlArea, self, "Find Root", addToLayout=False,
484                                           callback=lambda :self.rootNode and \
485                                           self.sceneView.centerOn(self.rootNode.x(), self.rootNode.y()))
486        self.centerNodeButton=OWGUI.button(self.controlArea, self, "Find Selected", addToLayout=False,
487                                           callback=lambda :self.selectedNode and \
488                                           self.sceneView.centerOn(self.selectedNode.scenePos()))
489        grid.addWidget(navButton, 0, 0, 1, 2)
490        grid.addWidget(self.centerRootButton, 1, 0)
491        grid.addWidget(self.centerNodeButton, 1, 1)
492        self.leftWidgetPart.layout().insertLayout(1, grid)
493       
494        self.NodeTab=NodeTab
495        self.TreeTab=TreeTab
496        self.GeneralTab=GeneralTab
497#        OWGUI.rubber(NodeTab)
498        self.rootNode=None
499        self.tree=None
500        self.resize(800, 500)
501       
502        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveGraph)
503
504    def sendReport(self):
505        from PyQt4.QtSvg import QSvgGenerator
506        if self.tree:
507            self.reportSection("Tree")
508            urlfn, filefn = self.getUniqueImageName(ext=".svg")
509            svg = QSvgGenerator()
510            svg.setFileName(filefn)
511            ssize = self.scene.sceneRect().size()
512            w, h = ssize.width(), ssize.height()
513            fact = 600/w
514            svg.setSize(QSize(600, h*fact))
515            painter = QPainter()
516            painter.begin(svg)
517            self.scene.render(painter)
518            painter.end()
519       
520#            buffer = QPixmap(QSize(600, h*fact))
521#            painter.begin(buffer)
522#            painter.fillRect(buffer.rect(), QBrush(QColor(255, 255, 255)))
523#            self.scene.render(painter)
524#            painter.end()
525#            self.reportImage(lambda filename: buffer.save(filename, os.path.splitext(filename)[1][1:]))
526            from OWDlgs import OWChooseImageSizeDlg
527            self.reportImage(OWChooseImageSizeDlg(self.scene).saveImage)
528            self.reportRaw('<!--browsercode<br/>(Click <a href="%s">here</a> to view or download this image in a scalable vector format)-->' % urlfn)
529            #self.reportObject(self.svg_type, urlfn, width="600", height=str(h*fact))
530
531    def toggleZoomSlider(self):
532        k = 0.0028 * (self.Zoom ** 2) + 0.2583 * self.Zoom + 1.1389
533        self.sceneView.setTransform(QTransform().scale(k/2, k/2))
534        self.scene.update()
535
536    def toggleVSpacing(self):
537        self.rescaleTree()
538        self.scene.fixPos(self.rootNode,10,10)
539        self.scene.update()
540
541    def toggleHSpacing(self):
542        self.rescaleTree()
543        self.scene.fixPos(self.rootNode,10,10)
544        self.scene.update()
545
546    def toggleTreeDepth(self):
547        self.walkupdate(self.rootNode)
548        self.scene.fixPos(self.rootNode,10,10)
549        self.scene.update()
550
551    def toggleLineWidth(self):
552        for edge in self.scene.edges():
553            if self.LineWidthMethod==0:
554                width=5 # self.LineWidth
555            elif self.LineWidthMethod == 1:
556                width = (edge.node2.tree.distribution.cases/self.tree.distribution.cases) * 20 # self.LineWidth
557            elif self.LineWidthMethod == 2:
558                width = (edge.node2.tree.distribution.cases/edge.node1.tree.distribution.cases) * 10 # self.LineWidth
559
560            edge.setPen(QPen(Qt.gray, width, Qt.SolidLine, Qt.RoundCap))
561        self.scene.update()
562       
563    def toggleNodeSize(self):
564        pass
565   
566    def toggleTruncateText(self):
567        for n in self.scene.nodes():
568            n.truncateText = self.TruncateText
569        self.scene.fixPos(self.rootNode, 10, 10)
570
571    def toggleNavigator(self):
572        self.navWidget.setHidden(not self.navWidget.isHidden())
573
574    def activateLoadedSettings(self):
575        if not self.tree:
576            return
577        self.rescaleTree()
578        self.scene.fixPos(self.rootNode,10,10)
579        self.scene.update()
580        self.toggleTreeDepth()
581        self.toggleLineWidth()
582#        self.toggleNodeSize()
583
584    def ctree(self, tree=None):
585        self.clear()
586        if not tree:
587            self.centerRootButton.setDisabled(1)
588            self.centerNodeButton.setDisabled(0)
589            self.infoa.setText('No tree.')
590            self.infob.setText('')
591            self.tree=None
592            self.rootNode = None
593        else:
594            self.tree=tree.tree
595            self.infoa.setText('Number of nodes: ' + str(orngTree.countNodes(tree)))
596            self.infob.setText('Number of leaves: ' + str(orngTree.countLeaves(tree)))
597            if hasattr(self.scene, "colorPalette"):
598                self.scene.colorPalette.setNumberOfColors(len(self.tree.distribution))
599#            self.scene.setDataModel(GraphicsTree(self.tree))
600            self.rootNode=self.walkcreate(self.tree, None)
601#            self.scene.addItem(self.rootNode)
602            self.scene.fixPos(self.rootNode,self.HSpacing,self.VSpacing)
603            self.activateLoadedSettings()
604            self.sceneView.centerOn(self.rootNode.x(), self.rootNode.y())
605            self.updateNodeToolTips()
606            self.centerRootButton.setDisabled(0)
607            self.centerNodeButton.setDisabled(1)
608
609        self.scene.update()
610
611    def walkcreate(self, tree, parent=None, level=0):
612        node = GraphicsNode(tree, parent, None, self.scene)
613        node.borderRadius = 10
614        if parent:
615            parent.graph_add_edge(GraphicsEdge(None, self.scene, node1=parent, node2=node))
616        if tree.branches:
617            for i in range(len(tree.branches)):
618                if tree.branches[i]:
619                    self.walkcreate(tree.branches[i],node,level+1)
620        return node
621
622    def walkupdate(self, node, level=0):
623        if not node: return
624        if self.MaxTreeDepthB and self.MaxTreeDepth<=level+1:
625            node.setOpen(False)
626            return
627        else:
628            node.setOpen(True,1)
629        for n in node.branches:
630            self.walkupdate(n,level+1)
631
632    def clear(self):
633        self.tree=None
634        self.scene.clear()
635
636    def updateNodeToolTips(self):
637       
638        for node in self.scene.nodes():
639            node.setToolTip(self.nodeToolTip(node) if self.ToolTipsEnabled else "")
640           
641    def nodeToolTip(self, tree):
642        return "tree node"
643   
644    def rescaleTree(self):
645        NodeHeight = DefNodeHeight
646        NodeWidth = DefNodeWidth * ((self.NodeSize -1) * (1.5 / 9.0) + 0.5)
647        k = 1.0
648        self.scene.VSpacing=int(NodeHeight*k*(0.3+self.VSpacing*0.15))
649        self.scene.HSpacing=int(NodeWidth*k*(0.3+self.HSpacing*0.20))
650        for r in self.scene.nodeList:
651            r.setRect(r.rect().x(), r.rect().y(), int(NodeWidth*k), int(NodeHeight*k))
652       
653        self.scene.fixPos() #self.rootNode, 10, 10)
654
655    def updateSelection(self):
656        self.selectedNode = (self.scene.selectedItems() + [None])[0]
657        self.centerNodeButton.setDisabled(not self.selectedNode)
658        self.send("Data", self.selectedNode.tree.examples if self.selectedNode else None)
659
660    def saveGraph(self, fileName = None):
661        from OWDlgs import OWChooseImageSizeDlg
662        dlg = OWChooseImageSizeDlg(self.scene, [("Save as Dot Tree File (.dot)", self.saveDot)], parent=self)
663        dlg.exec_()
664       
665    def saveDot(self, filename=None):
666        if filename==None:
667            filename = QFileDialog.getSaveFileName(self, "Save to ...", "tree.dot", "Dot Tree File (.DOT)")
668            filename = unicode(filename)
669            if not filename:
670                return
671        orngTree.printDot(self.tree, filename)
672       
673class OWDefTreeViewer2D(OWTreeViewer2D):
674    def __init__(self, parent=None, signalManager = None, name='DefTreeViewer2D'):
675        OWTreeViewer2D.__init__(self, parent, signalManager, name)
676        self.settingsList=self.settingsList+["ShowPie"]
677
678        self.scene = TreeGraphicsScene(self)
679        self.sceneView = TreeGraphicsView(self, self.scene, self.mainArea)
680        self.mainArea.layout().addWidget(self.sceneView)
681#        self.scene.setSceneRect(0,0,800,800)
682        self.navWidget = QWidget(None)
683        self.navWidget.setLayout(QVBoxLayout(self.navWidget))
684        scene = TreeGraphicsScene(self.navWidget)
685        self.treeNav = TreeNavigator(self.sceneView)
686#        self.treeNav.setScene(scene)
687        self.navWidget.layout().addWidget(self.treeNav)
688#        self.sceneView.setNavigator(self.treeNav)
689        self.navWidget.resize(400,400)
690#        OWGUI.button(self.TreeTab,self,"Navigator",self.toggleNavigator)
691
692if __name__=="__main__":
693    a = QApplication(sys.argv)
694    ow = OWDefTreeViewer2D()
695
696    #data = orange.ExampleTable('../../doc/datasets/voting.tab')
697    data = orange.ExampleTable(r"..//doc//datasets//zoo.tab")
698    data = orange.ExampleTable(r"..//doc//datasets//iris.tab")
699    tree = orange.TreeLearner(data, storeExamples = 1)
700    ow.activateLoadedSettings()
701    ow.ctree(None)
702    ow.ctree(tree)
703
704    # here you can test setting some stuff
705    ow.show()
706    a.exec_()
707    ow.saveSettings()
708
Note: See TracBrowser for help on using the repository browser.