squaremap.py :  » Project-Management » Task-Coach » TaskCoach-1.0.3 » taskcoachlib » thirdparty » squaremap » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Project Management » Task Coach 
Task Coach » TaskCoach 1.0.3 » taskcoachlib » thirdparty » squaremap » squaremap.py
#! /usr/bin/env python
import wx, sys, os
import wx.lib.newevent

SquareHighlightEvent, EVT_SQUARE_HIGHLIGHTED = wx.lib.newevent.NewEvent()
SquareSelectionEvent, EVT_SQUARE_SELECTED = wx.lib.newevent.NewEvent()
SquareActivationEvent, EVT_SQUARE_ACTIVATED = wx.lib.newevent.NewEvent()


class HotMapNavigator(object):
    ''' Utility class for navigating the hot map and finding nodes. '''

    @classmethod
    def findNode(class_, hot_map, targetNode, parentNode=None):
        ''' Find the target node in the hot_map. '''
        for index, (rect, node, children) in enumerate(hot_map):
            if node == targetNode:
                return parentNode, hot_map, index
            result = class_.findNode(children, targetNode, node)
            if result:
                return result
        return None

    @classmethod
    def findNodeAtPosition(class_, hot_map, position, parent=None):
        ''' Retrieve the node at the given position. '''
        for rect, node, children in hot_map:
            if rect.Contains(position):
                return class_.findNodeAtPosition(children, position, node)
        return parent

    @staticmethod
    def firstChild(hot_map, index):
        ''' Return the first child of the node indicated by index. '''
        children = hot_map[index][2]
        if children:
            return children[0][1]
        else:
            return hot_map[index][1] # No children, return the node itself
        
    @staticmethod
    def nextChild(hotmap, index):
        ''' Return the next sibling of the node indicated by index. '''
        nextChildIndex = min(index + 1, len(hotmap) - 1)
        return hotmap[nextChildIndex][1]
    
    @staticmethod
    def previousChild(hotmap, index):
        ''' Return the previous sibling of the node indicated by index. '''
        previousChildIndex = max(0, index - 1)
        return hotmap[previousChildIndex][1]

    @staticmethod
    def firstNode(hot_map):
        ''' Return the very first node in the hot_map. '''
        return hot_map[0][1]
    
    @classmethod
    def lastNode(class_, hot_map):
        ''' Return the very last node (recursively) in the hot map. '''
        children = hot_map[-1][2]
        if children:
            return class_.lastNode(children)
        else:
            return hot_map[-1][1] # Return the last node
    
    
class SquareMap( wx.Panel ):
    """Construct a nested-box trees structure view"""

    BackgroundColor = wx.Color( 128,128,128 )
    
    def __init__( 
        self,  parent=None, id=-1, pos=wx.DefaultPosition, 
        size=wx.DefaultSize, 
        style=wx.TAB_TRAVERSAL|wx.NO_BORDER|wx.FULL_REPAINT_ON_RESIZE, 
        name='SquareMap', model = None,
        adapter = None,
        labels = True, # set to True to draw textual labels within the boxes
        highlight = True, # set to False to turn of highlighting
        padding = 2, # amount to reduce the children's box from the parent's box
    ):
        super( SquareMap, self ).__init__(
            parent, id, pos, size, style, name
        )
        self.model = model
        self.padding = padding
        self.labels = labels
        self.highlight = highlight
        self.selectedNode = None
        self.highlightedNode = None
        self._buffer = wx.EmptyBitmap(20, 20) # Have a default buffer ready
        self.Bind( wx.EVT_PAINT, self.OnPaint)
        self.Bind( wx.EVT_SIZE, self.OnSize )
        if highlight:
            self.Bind( wx.EVT_MOTION, self.OnMouse )
        self.Bind( wx.EVT_LEFT_UP, self.OnClickRelease )
        self.Bind( wx.EVT_LEFT_DCLICK, self.OnDoubleClick )
        self.Bind( wx.EVT_KEY_UP, self.OnKeyUp )
        self.hot_map = []
        self.adapter = adapter or DefaultAdapter()
        self.DEFAULT_PEN = wx.Pen( wx.BLACK, 1, wx.SOLID )
        self.SELECTED_PEN = wx.Pen( wx.WHITE, 2, wx.SOLID )
        self.OnSize(None)
        
    def OnMouse( self, event ):
        """Handle mouse-move event by selecting a given element"""
        node = HotMapNavigator.findNodeAtPosition(self.hot_map, event.GetPosition())
        self.SetHighlight( node, event.GetPosition() )

    def OnClickRelease( self, event ):
        """Release over a given square in the map"""
        node = HotMapNavigator.findNodeAtPosition(self.hot_map, event.GetPosition())
        self.SetSelected( node, event.GetPosition() )
        
    def OnDoubleClick(self, event):
        """Double click on a given square in the map"""
        node = HotMapNavigator.findNodeAtPosition(self.hot_map, event.GetPosition())
        if node:
            wx.PostEvent( self, SquareActivationEvent( node=node, point=event.GetPosition(), map=self ) )
    
    def OnKeyUp(self, event):
        event.Skip()
        if not self.selectedNode or not self.hot_map:
            return
        
        if event.KeyCode == wx.WXK_HOME:
            self.SetSelected(HotMapNavigator.firstNode(self.hot_map))
            return
        elif event.KeyCode == wx.WXK_END:
            self.SetSelected(HotMapNavigator.lastNode(self.hot_map))
            return
        
        parent, children, index = HotMapNavigator.findNode(self.hot_map, self.selectedNode)
        if event.KeyCode == wx.WXK_DOWN:
            self.SetSelected(HotMapNavigator.nextChild(children, index))
        elif event.KeyCode == wx.WXK_UP:
            self.SetSelected(HotMapNavigator.previousChild(children, index))
        elif event.KeyCode == wx.WXK_RIGHT:
            self.SetSelected(HotMapNavigator.firstChild(children, index))
        elif event.KeyCode == wx.WXK_LEFT and parent:
            self.SetSelected(parent)
        elif event.KeyCode == wx.WXK_RETURN:
            wx.PostEvent(self, SquareActivationEvent(node=self.selectedNode,
                                                     map=self))
            
    def GetSelected(self):
        return self.selectedNode
            
    def SetSelected( self, node, point=None, propagate=True ):
        """Set the given node selected in the square-map"""
        if node == self.selectedNode:
            return
        self.selectedNode = node 
        self.Refresh()
        if node:
            wx.PostEvent( self, SquareSelectionEvent( node=node, point=point, map=self ) )

    def SetHighlight( self, node, point=None, propagate=True ):
        """Set the currently-highlighted node"""
        if node == self.highlightedNode:
            return
        self.highlightedNode = node 
        self.Refresh()
        if node and propagate:
            wx.PostEvent( self, SquareHighlightEvent( node=node, point=point, map=self ) )

    def SetModel( self, model, adapter=None ):
        """Set our model object (root of the tree)"""
        self.model = model
        if adapter is not None:
            self.adapter = adapter
        self.Refresh()
        
    def Refresh(self):
        self.UpdateDrawing()
    
    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self._buffer)

    def OnSize(self, event):
        # The buffer is initialized in here, so that the buffer is always
        # the same size as the Window.
        width, height = self.GetClientSizeTuple()
        if width <= 0 or height <= 0:
            return
        # Make new off-screen bitmap: this bitmap will always have the
        # current drawing in it, so it can be used to save the image to
        # a file, or whatever.
        self._buffer = wx.EmptyBitmap(width, height)
        self.UpdateDrawing()

    def UpdateDrawing(self):
        dc = wx.BufferedDC(wx.ClientDC(self), self._buffer)
        self.Draw(dc)
        
    def Draw(self, dc):
        ''' Draw the tree map on the device context. '''
        self.hot_map = []
        dc.BeginDrawing()
        brush = wx.Brush( self.BackgroundColor  )
        dc.SetBackground( brush )
        dc.Clear()
        if self.model:
            w, h = dc.GetSize()
            self.DrawBox( dc, self.model, 0,0,w,h, hot_map = self.hot_map )
        dc.EndDrawing()
        
    def BrushForNode( self, node, depth=0 ):
        """Create brush to use to display the given node"""
        if node == self.selectedNode:
            color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
        elif node == self.highlightedNode:
            color = wx.Color( red=0, green=255, blue=0 )
        else:
            color = self.adapter.background_color(node, depth)
            if not color:
                red = (depth * 10)%255
                green = 255-((depth * 10)%255)
                blue = 200
                color = wx.Color( red, green, blue )
        return wx.Brush( color  )
    
    def PenForNode( self, node, depth=0 ):
        """Determine the pen to use to display the given node"""
        if node == self.selectedNode:
            return self.SELECTED_PEN
        return self.DEFAULT_PEN

    def TextForegroundForNode(self, node, depth=0):
        """Determine the text foreground color to use to display the label of
           the given node"""
        if node == self.selectedNode:
            fg_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
        else:
            fg_color = self.adapter.foreground_color(node, depth)
            if not fg_color:
                fg_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
        return fg_color
    
    def FontForNode(self, dc, node, depth=0):
        """Determine the font to use to display the label of the given node,
           scaled for printing if necessary"""
        font = self.adapter.font(node, depth)
        font = font if font else wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
        scale = dc.GetPPI()[0] / wx.ScreenDC().GetPPI()[0]
        font.SetPointSize(scale*font.GetPointSize())
        return font
    
    def DrawBox( self, dc, node, x,y,w,h, hot_map, depth=0 ):
        """Draw a model-node's box and all children nodes"""
        dc.SetBrush( self.BrushForNode( node, depth ) )
        dc.SetPen( self.PenForNode( node, depth ) )
        dc.DrawRoundedRectangle( x,y,w,h, self.padding *3 )
#        self.DrawIconAndLabel(dc, node, x, y, w, h, depth)
        children_hot_map = []
        hot_map.append( (wx.Rect( int(x),int(y),int(w),int(h)), node, children_hot_map ) )
        x += self.padding
        y += self.padding
        w -= self.padding*2
        h -= self.padding*2
        
        empty = self.adapter.empty( node )
        icon_drawn = False
        if empty:
            # is a fraction of the space which is empty...
            new_h = h * (1.0-empty)
            self.DrawIconAndLabel(dc, node, x, y, w, h-new_h, depth)
            icon_drawn = True
            y += (h-new_h)
            h = new_h
        
        if w >self.padding*2 and h> self.padding*2:
            children = self.adapter.children( node )
            if children:
                self.LayoutChildren( dc, children, node, x,y,w,h, children_hot_map, depth+1 )
            else:
                if not icon_drawn:
                    self.DrawIconAndLabel(dc, node, x, y, w, h, depth)
                
    def DrawIconAndLabel(self, dc, node, x, y, w, h, depth):
        ''' Draw the icon, if any, and the label, if any, of the node. '''
        dc.SetClippingRegion(x+1, y+1, w-2, h-2) # Don't draw outside the box
        icon = self.adapter.icon(node, node==self.selectedNode)
        if icon and h >= icon.GetHeight() and w >= icon.GetWidth():
            iconWidth = icon.GetWidth() + 2
            dc.DrawIcon(icon, x+2, y+2) 
        else:
            iconWidth = 0
        if self.labels and h >= dc.GetTextExtent('ABC')[1]:
            dc.SetFont(self.FontForNode(dc, node, depth))
            dc.SetTextForeground(self.TextForegroundForNode(node, depth))
            dc.DrawText(self.adapter.label(node), x + iconWidth + 2, y+2)
        dc.DestroyClippingRegion()
        
    def LayoutChildren( self, dc, children, parent, x,y,w,h, hot_map, depth=0 ):
        """Layout the set of children in the given rectangle"""
        nodes = [ (self.adapter.value(node,parent),node) for node in children ]
        nodes.sort()
        total = self.adapter.children_sum( children,parent )
        if total:
            (firstSize,firstNode) = nodes[-1]
            rest = [node for (size,node) in nodes[:-1]]
            fraction = firstSize/float(total)
            if w >= h:
                new_w = int(w*fraction)
                if new_w:
                    self.DrawBox( dc, firstNode, x,y, new_w, h, hot_map, depth+1 )
                else:
                    return # no other node will show up as non-0 either
                w = w-new_w
                x += new_w 
            else:
                new_h = int(h*fraction)
                if new_h:
                    self.DrawBox( dc, firstNode, x,y, w, new_h, hot_map, depth + 1 )
                else:
                    return # no other node will show up as non-0 either
                h = h-new_h
                y += new_h 
            if rest and (h > self.padding*2) and (w > self.padding*2):
                self.LayoutChildren( dc, rest, parent, x,y,w,h, hot_map, depth )


class DefaultAdapter( object ):
    """Default adapter class for adapting node-trees to SquareMap API"""
    def children( self, node ):
        """Retrieve the set of nodes which are children of this node"""
        return node.children
    def value( self, node, parent=None ):
        """Return value used to compare size of this node"""
        return node.size
    def label( self, node ):
        """Return textual description of this node"""
        return node.path
    def overall( self, node ):
        """Calculate overall size of the node including children and empty space"""
        return sum( [self.value(value,node) for value in self.children(node)] )
    def children_sum( self, children,node ):
        """Calculate children's total sum"""
        return sum( [self.value(value,node) for value in children] )
    def empty( self, node ):
        """Calculate empty space as a fraction of total space"""
        overall = self.overall( node )
        if overall:
            return (overall - self.children_sum( self.children(node), node))/float(overall)
        return 0
    def background_color(self, node, depth):
        ''' The color to use as background color of the node. '''
        return None
    def foreground_color(self, node, depth):
        ''' The color to use for the label. '''
        return None
    def font(self, node, depth):
        ''' The font to use for the label. '''
        return None
    def icon(self, node, isSelected):
        ''' The icon to display in the node. '''
        return None
    def parents( self, node ):
        """Retrieve/calculate the set of parents for the given node"""
        return []


class TestApp(wx.App):
    """Basic application for holding the viewing Frame"""
    def OnInit(self):
        """Initialise the application"""
        wx.InitAllImageHandlers()
        self.frame = frame = wx.Frame( None,
        )
        frame.CreateStatusBar()
        
        model = model = self.get_model( sys.argv[1]) 
        self.sq = SquareMap( frame, model=model)
        EVT_SQUARE_HIGHLIGHTED( self.sq, self.OnSquareSelected )
        frame.Show(True)
        self.SetTopWindow(frame)
        return True
    def get_model( self, path ):
        nodes = []
        for f in os.listdir( path ):
            full = os.path.join( path,f )
            if not os.path.islink( full ):
                if os.path.isfile( full ):
                    nodes.append( Node( full, os.stat( full ).st_size, () ) )
                elif os.path.isdir( full ):
                    nodes.append( self.get_model( full ))
        return Node( path, sum([x.size for x in nodes]), nodes )
    def OnSquareSelected( self, event ):
        self.frame.SetStatusText( self.sq.adapter.label( event.node ) )

class Node( object ):
    """Really dumb file-system node object"""
    def __init__( self, path, size, children ):
        self.path = path
        self.size = size
        self.children = children 
    def __repr__( self ):
        return '%s( %r, %r, %r )'%( self.__class__.__name__, self.path, self.size, self.children )
        

usage = 'squaremap.py somedirectory'
        
def main():
    """Mainloop for the application"""
    if not sys.argv[1:]:
        print usage
    else:
        app = TestApp(0)
        app.MainLoop()

if __name__ == "__main__":
    main()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.