Changeset 2424

Show
Ignore:
Timestamp:
09/29/08 23:20:38 (2 months ago)
Author:
gaph..@gmail.com
Message:
  • Separated indexing logic from tree.Tree (to class tree.TreeIndexer?)
  • Painters no longer need to sort selected items
  • view.GtkView?.selected_items is a list now.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphas/trunk/gaphas/canvas.py

    r2340 r2424  
    4242    def __init__(self): 
    4343        self._tree = tree.Tree() 
     44        self._tree_indexer = tree.TreeIndexer(self._tree, '_canvas_index') 
    4445        self._solver = solver.Solver() 
    4546        self._dirty_items = set() 
     
    304305        True 
    305306        """ 
    306         return self._tree.sort(items, index_key='_canvas_index', reverse=reverse) 
     307        return self._tree_indexer.sort(items, reverse=reverse) 
    307308 
    308309 
     
    606607        for fast searching of items. 
    607608        """ 
    608         self._tree.index_nodes('_canvas_index'
     609        self._tree_indexer.index_tree(
    609610 
    610611 
  • gaphas/trunk/gaphas/painter.py

    r2328 r2424  
    259259            self._draw_item(item, view, cairo) 
    260260 
     261 
    261262    def paint(self, context): 
    262263        cairo = context.cairo 
     
    271272    """ 
    272273 
    273     def _draw_handles(self, item, view, cairo, opacity=None): 
     274    def _draw_handles(self, item, view, cairo, opacity): 
    274275        """ 
    275276        Draw handles for an item. 
     
    278279        cairo.save() 
    279280        i2v = view.get_matrix_i2v(item) 
    280         if not opacity: 
    281             opacity = (item is view.focused_item) and .7 or .4 
    282281 
    283282        cairo.set_line_width(1) 
     
    312311        canvas = view.canvas 
    313312        cairo = context.cairo 
    314         # Order matters here: 
    315         for item in canvas.sort(view.selected_items): 
    316             self._draw_handles(item, view, cairo) 
     313 
     314        # Selected items are already ordered: 
     315        focused = view.focused_item 
     316        for item in view.selected_items: 
     317            self._draw_handles(item, view, cairo, 
     318                    opacity=(item is focused) and .7 or .4) 
     319 
    317320        # Draw nice opaque handles when hovering an item: 
    318         item = view.hovered_item 
    319         if item and item not in view.selected_items
    320             self._draw_handles(item, view, cairo, opacity=.25) 
     321        hovered = view.hovered_item 
     322        if hovered
     323            self._draw_handles(hovered, view, cairo, opacity=.25) 
    321324 
    322325 
  • gaphas/trunk/gaphas/tests/test_view.py

    r1840 r2424  
    1313 
    1414class ViewTestCase(unittest.TestCase): 
     15 
     16    def test_selecting_items(self): 
     17        """ 
     18        Test selection of items 
     19        """ 
     20        canvas = Canvas() 
     21        view = GtkView(canvas) 
     22        window = gtk.Window(gtk.WINDOW_TOPLEVEL) 
     23        window.add(view) 
     24        window.show_all() 
     25 
     26        box = Box() 
     27        canvas.add(box) 
     28        view.focused_item = box 
     29        assert view.focused_item is box 
     30        assert len(view.selected_items) == 1 
     31 
     32        view.select_item(box) 
     33        assert len(view.selected_items) == 1 
     34 
     35        view.unselect_all() 
     36        assert len(view.selected_items) == 0 
     37        assert view.focused_item is None 
     38 
    1539 
    1640    def test_bounding_box_calculations(self): 
  • gaphas/trunk/gaphas/tree.py

    r2328 r2424  
    159159            parent = self.get_parent(parent) 
    160160 
    161     def index_nodes(self, index_key): 
     161    def _add_to_nodes(self, node, parent): 
     162        """ 
     163        Called only from add() 
     164        """ 
     165        nodes = self._nodes 
     166        if parent: 
     167            try: 
     168                next_uncle = self.get_next_sibling(parent) 
     169            except IndexError: 
     170                # parent has no younger brothers.. 
     171                # place it before the next uncle of grant_parent: 
     172                self._add_to_nodes(node, self.get_parent(parent)) 
     173            else: 
     174                nodes.insert(nodes.index(next_uncle), node) 
     175        else: 
     176            # append to root node: 
     177            nodes.append(node) 
     178 
     179    @observed 
     180    def add(self, node, parent=None): 
     181        """ 
     182        Add node to the tree. parent is the parent node, which may 
     183        be None if the item should be added to the root item. 
     184 
     185        For usage, see the unit tests. 
     186        """ 
     187        assert not self._children.get(node) 
     188        siblings = self._children[parent] 
     189        self._add_to_nodes(node, parent) 
     190        siblings.append(node) 
     191        # Create new entry for it's own children: 
     192        self._children[node] = [] 
     193        if parent: 
     194            self._parents[node] = parent 
     195 
     196    @observed 
     197    def _remove(self, node): 
     198        # Remove from parent item 
     199        self.get_siblings(node).remove(node) 
     200        # Remove data entries: 
     201        del self._children[node] 
     202        self._nodes.remove(node) 
     203        try: 
     204            del self._parents[node] 
     205        except KeyError: 
     206            pass 
     207 
     208    def remove(self, node): 
     209        """ 
     210        Remove ``node`` from the tree. 
     211 
     212        For usage, see the unit tests. 
     213        """ 
     214        # First remove children: 
     215        for c in reversed(list(self._children[node])): 
     216            self.remove(c) 
     217        self._remove(node) 
     218 
     219    def _reparent_nodes(self, node, parent): 
     220        """ 
     221        Helper for reparent(). 
     222        The _children and _parent trees can be left intact as far as children 
     223        of the reparented node are concerned. Only the position in the 
     224        _nodes list changes. 
     225        """ 
     226        self._nodes.remove(node) 
     227        self._add_to_nodes(node, parent) 
     228        for c in self._children[node]: 
     229            self._reparent_nodes(c, node) 
     230         
     231    @observed 
     232    def reparent(self, node, parent): 
     233        """ 
     234        Set new parent for a ``node``. ``Parent`` can be ``None``, indicating 
     235        it's added to the top. 
     236 
     237        >>> tree = Tree() 
     238        >>> tree.add('n1') 
     239        >>> tree.add('n2', parent='n1') 
     240        >>> tree.add('n3', parent='n1') 
     241        >>> tree.nodes 
     242        ['n1', 'n2', 'n3'] 
     243        >>> tree.reparent('n2', 'n3') 
     244        >>> tree.get_parent('n2') 
     245        'n3' 
     246        >>> tree.get_children('n3') 
     247        ['n2'] 
     248        >>> tree.nodes 
     249        ['n1', 'n3', 'n2'] 
     250 
     251        If a node contains children, those are also moved: 
     252         
     253        >>> tree.add('n4') 
     254        >>> tree.nodes 
     255        ['n1', 'n3', 'n2', 'n4'] 
     256        >>> tree.reparent('n1', 'n4') 
     257        >>> tree.get_parent('n1') 
     258        'n4' 
     259        >>> list(tree.get_all_children('n4')) 
     260        ['n1', 'n3', 'n2'] 
     261        >>> tree.nodes 
     262        ['n4', 'n1', 'n3', 'n2'] 
     263        """ 
     264        # Add to new parent's children: 
     265        self.get_siblings(node).remove(node) 
     266        self._children[parent].append(node) 
     267        self._parents[node] = parent 
     268         
     269        # reorganize nodes 
     270        self._reparent_nodes(node, parent) 
     271 
     272 
     273    reversible_pair(add, _remove, 
     274            bind1={'parent': lambda self, node: self.get_parent(node) }) 
     275 
     276    reversible_method(reparent, reverse=reparent, 
     277            bind={'parent': lambda self, node: self.get_parent(node) }) 
     278 
     279    # Disable add/remove by default, since they are handled by canvas.Canvas 
     280    disable_dispatching(add) 
     281    disable_dispatching(_remove) 
     282 
     283 
     284class TreeIndexer(object): 
     285    """ 
     286    The ``TreeIndexer`` is a straight-forward indexer that adds an 
     287    index-attribute to each object in the tree. This attribute can be used 
     288    to do fast sorting. 
     289    """ 
     290 
     291    def __init__(self, tree, index_key): 
     292        self._tree = tree 
     293        self._index_key = index_key 
     294 
     295 
     296    def index_tree(self): 
    162297        """ 
    163298        Provide each item in the tree with an index attribute. This makes 
     
    176311        >>> t.nodes 
    177312        [a, c, b] 
    178         >>> t.index_nodes('my_key') 
     313        >>> idx = TreeIndexer(t, 'my_key') 
     314        >>> idx.index_tree() 
    179315        >>> t.nodes[0].my_key, t.nodes[1].my_key, t.nodes[2].my_key 
    180316        (0, 1, 2) 
     
    182318        For sorting, see ``sort()``. 
    183319        """ 
    184         nodes = self.nodes 
     320        nodes = self._tree.nodes 
    185321        lnodes = len(nodes) 
    186         map(setattr, nodes, [index_key] * lnodes, xrange(lnodes)) 
    187  
    188     def sort(self, nodes, index_key, reverse=False): 
    189         """ 
    190         Sort a set (or list) of nodes. 
     322        map(setattr, nodes, [self._index_key] * lnodes, xrange(lnodes)) 
     323 
     324 
     325    def sort(self, nodes, reverse=False): 
     326        """ 
     327        Sort a set (or list) of nodes to the order defined by the ``Tree``. 
    191328         
    192329        >>> class A(object): 
     
    202339        >>> t.nodes    # the series from Tree.index_nodes 
    203340        [a, c, b] 
    204         >>> t.index_nodes('my_key') 
     341        >>> idx = TreeIndexer(t, 'my_key') 
     342        >>> idx.index_tree() 
    205343        >>> selection = (t.nodes[2], t.nodes[1]) 
    206         >>> t.sort(selection, index_key='my_key'
     344        >>> idx.sort(selection
    207345        [c, b] 
    208         """ 
    209         if index_key: 
    210             return sorted(nodes, key=attrgetter(index_key), reverse=reverse) 
    211         else: 
    212             raise NotImplemented('index_key should be provided.') 
    213  
    214     def _add_to_nodes(self, node, parent): 
    215         """ 
    216         Called only from add() 
    217         """ 
    218         nodes = self._nodes 
    219         if parent: 
    220             try: 
    221                 next_uncle = self.get_next_sibling(parent) 
    222             except IndexError: 
    223                 # parent has no younger brothers.. 
    224                 # place it before the next uncle of grant_parent: 
    225                 self._add_to_nodes(node, self.get_parent(parent)) 
    226             else: 
    227                 nodes.insert(nodes.index(next_uncle), node) 
    228         else: 
    229             # append to root node: 
    230             nodes.append(node) 
    231  
    232     @observed 
    233     def add(self, node, parent=None): 
    234         """ 
    235         Add node to the tree. parent is the parent node, which may 
    236         be None if the item should be added to the root item. 
    237  
    238         For usage, see the unit tests. 
    239         """ 
    240         assert not self._children.get(node) 
    241         siblings = self._children[parent] 
    242         self._add_to_nodes(node, parent) 
    243         siblings.append(node) 
    244         # Create new entry for it's own children: 
    245         self._children[node] = [] 
    246         if parent: 
    247             self._parents[node] = parent 
    248  
    249     @observed 
    250     def _remove(self, node): 
    251         # Remove from parent item 
    252         self.get_siblings(node).remove(node) 
    253         # Remove data entries: 
    254         del self._children[node] 
    255         self._nodes.remove(node) 
     346 
     347        If the tree is not yet indexed, it is indexed on the fly. 
     348 
     349        >>> idx = TreeIndexer(t, 'my_key') 
     350        >>> selection = (t.nodes[2], t.nodes[1]) 
     351        >>> idx.sort(selection) 
     352        [c, b] 
     353        """ 
    256354        try: 
    257             del self._parents[node] 
    258         except KeyError: 
    259             pass 
    260  
    261     def remove(self, node): 
    262         """ 
    263         Remove ``node`` from the tree. 
    264  
    265         For usage, see the unit tests. 
    266         """ 
    267         # First remove children: 
    268         for c in reversed(list(self._children[node])): 
    269             self.remove(c) 
    270         self._remove(node) 
    271  
    272     def _reparent_nodes(self, node, parent): 
    273         """ 
    274         Helper for reparent(). 
    275         The _children and _parent trees can be left intact as far as children 
    276         of the reparented node are concerned. Only the position in the 
    277         _nodes list changes. 
    278         """ 
    279         self._nodes.remove(node) 
    280         self._add_to_nodes(node, parent) 
    281         for c in self._children[node]: 
    282             self._reparent_nodes(c, node) 
    283          
    284     @observed 
    285     def reparent(self, node, parent): 
    286         """ 
    287         Set new parent for a ``node``. ``Parent`` can be ``None``, indicating 
    288         it's added to the top. 
    289  
    290         >>> tree = Tree() 
    291         >>> tree.add('n1') 
    292         >>> tree.add('n2', parent='n1') 
    293         >>> tree.add('n3', parent='n1') 
    294         >>> tree.nodes 
    295         ['n1', 'n2', 'n3'] 
    296         >>> tree.reparent('n2', 'n3') 
    297         >>> tree.get_parent('n2') 
    298         'n3' 
    299         >>> tree.get_children('n3') 
    300         ['n2'] 
    301         >>> tree.nodes 
    302         ['n1', 'n3', 'n2'] 
    303  
    304         If a node contains children, those are also moved: 
    305          
    306         >>> tree.add('n4') 
    307         >>> tree.nodes 
    308         ['n1', 'n3', 'n2', 'n4'] 
    309         >>> tree.reparent('n1', 'n4') 
    310         >>> tree.get_parent('n1') 
    311         'n4' 
    312         >>> list(tree.get_all_children('n4')) 
    313         ['n1', 'n3', 'n2'] 
    314         >>> tree.nodes 
    315         ['n4', 'n1', 'n3', 'n2'] 
    316         """ 
    317         # Add to new parent's children: 
    318         self.get_siblings(node).remove(node) 
    319         self._children[parent].append(node) 
    320         self._parents[node] = parent 
    321          
    322         # reorganize nodes 
    323         self._reparent_nodes(node, parent) 
    324  
    325  
    326     reversible_pair(add, _remove, 
    327             bind1={'parent': lambda self, node: self.get_parent(node) }) 
    328  
    329     reversible_method(reparent, reverse=reparent, 
    330             bind={'parent': lambda self, node: self.get_parent(node) }) 
    331  
    332     # Disable add/remove by default, since they are handled by canvas.Canvas 
    333     disable_dispatching(add) 
    334     disable_dispatching(_remove) 
     355            return sorted(nodes, key=attrgetter(self._index_key), reverse=reverse) 
     356        except AttributeError: 
     357            # Looks like not all elements are indexed, so let's do it instantly 
     358            self.index_tree() 
     359            return sorted(nodes, key=attrgetter(self._index_key), reverse=reverse) 
    335360 
    336361 
  • gaphas/trunk/gaphas/view.py

    r2328 r2424  
    3535 
    3636        # Handling selections. 
    37         self._selected_items = set() 
     37        self._selected_items = list() 
    3838        self._focused_item = None 
    3939        self._hovered_item = None 
     
    8181        self.queue_draw_item(item) 
    8282        if item not in self._selected_items: 
    83             self._selected_items.add(item) 
     83            self._selected_items.append(item) 
     84            self._selected_items = self._canvas.sort(self._selected_items) 
    8485            self.emit('selection-changed', self._selected_items) 
    8586 
     
    9192        self.queue_draw_item(item) 
    9293        if item in self._selected_items: 
    93             self._selected_items.discard(item) 
     94            self._selected_items.remove(item) 
    9495            self.emit('selection-changed', self._selected_items) 
    9596 
     
    105106        """ 
    106107        self.queue_draw_item(*self._selected_items) 
    107         self._selected_items.clear() 
     108        del self._selected_items[:] 
    108109        self.focused_item = None 
    109110        self.emit('selection-changed', self._selected_items) 
     
    566567            for item in removed_items: 
    567568                self._qtree.remove(item) 
    568                 self.selected_items.discard(item) 
     569                self.selected_items.remove(item) 
    569570 
    570571            if self.focused_item in removed_items: