tuikit/treeview.py
changeset 44 d77f1ae3786c
parent 43 369c8ef5070a
child 45 43b2279b06e1
equal deleted inserted replaced
43:369c8ef5070a 44:d77f1ae3786c
    15 
    15 
    16     This is useful for displaying the tree.
    16     This is useful for displaying the tree.
    17 
    17 
    18     """
    18     """
    19 
    19 
    20     def __init__(self, root, collapsed=[]):
    20     def __init__(self, root, collapsed_nodes=[]):
    21         self._node = root
    21         self._node = root
    22         self._index = 0
    22         self._index = 0
    23         self._stack = []
    23         self._stack = []
    24         self._collapsed = collapsed
    24         self._collapsed_nodes = collapsed_nodes
    25 
    25 
    26     def __next__(self):
    26     def __next__(self):
    27         node = None
    27         node = None
    28         while node is None:
    28         while node is None:
    29             try:
    29             try:
    30                 if self._node in self._collapsed:
    30                 if self._node in self._collapsed_nodes:
    31                     raise IndexError()
    31                     raise IndexError()
    32                 node = self._node.children[self._index]
    32                 node = self._node.children[self._index]
    33                 if node is None:
    33                 if node is None:
    34                     raise Exception('Bad node: None')
    34                     raise Exception('Bad node: None')
    35             except IndexError:
    35             except IndexError:
   175 class TreeView(Widget):
   175 class TreeView(Widget):
   176     """Tree view displays data from tree model."""
   176     """Tree view displays data from tree model."""
   177 
   177 
   178     def __init__(self, model=None, width=20, height=20):
   178     def __init__(self, model=None, width=20, height=20):
   179         Widget.__init__(self, width, height)
   179         Widget.__init__(self, width, height)
   180 
       
   181         self.allow_focus = True
   180         self.allow_focus = True
   182 
       
   183         # cursor
       
   184         self.cnode = None
       
   185 
   181 
   186         # model
   182         # model
   187         self._model = None
   183         self._model = None
   188 
   184 
   189         self.collapsed = []
   185         # cursor
       
   186         self._cursor_node = None
       
   187 
       
   188         self.collapsed_nodes = []
   190 
   189 
   191         self.add_events(
   190         self.add_events(
   192             'expand', TreeEvent,  # node expanded, event carries the affected node
   191             'expand', TreeEvent,  # node expanded, event carries the affected node
   193             'collapse', TreeEvent) # node collapsed, event carries the affected node
   192             'collapse', TreeEvent) # node collapsed, event carries the affected node
   194 
   193 
   195         if model:
   194         if model:
   196             self.model = model
   195             self.model = model
   197 
   196 
   198     def __iter__(self):
   197     def __iter__(self):
   199         return TreeIter(self._model.root, self.collapsed)
   198         return TreeIter(self._model.root, self.collapsed_nodes)
   200 
   199 
   201     @property
   200     @property
   202     def model(self):
   201     def model(self):
   203         """TreeModel in use by this TreeView."""
   202         """TreeModel in use by this TreeView."""
   204         return self._model
   203         return self._model
   209             self._model.disconnect('node_added', self.on_model_node_added)
   208             self._model.disconnect('node_added', self.on_model_node_added)
   210         self._model = value
   209         self._model = value
   211         if self._model:
   210         if self._model:
   212             self._model.connect('node_added', self.on_model_node_added)
   211             self._model.connect('node_added', self.on_model_node_added)
   213             try:
   212             try:
   214                 self.cnode = self._model.root.children[0]
   213                 self.cursor_node = self._model.root.children[0]
   215             except IndexError:
   214             except IndexError:
   216                 pass
   215                 pass
   217             self._update_sizereq()
   216             self._update_sizereq()
   218 
   217 
   219     def on_model_node_added(self, ev):
   218     def on_model_node_added(self, ev):
   220         if self.cnode is None:
   219         if self.cursor_node is None:
   221             self.cnode = ev.node
   220             self.cursor_node = ev.node
   222         self._update_sizereq()
   221         self._update_sizereq()
   223         self.redraw()
   222         self.redraw()
       
   223 
       
   224     @property
       
   225     def cursor_node(self):
       
   226         return self._cursor_node
       
   227 
       
   228     @cursor_node.setter
       
   229     def cursor_node(self, value):
       
   230         self._cursor_node = value
       
   231         self._update_spot()
   224 
   232 
   225     def collapse(self, path, collapse=True):
   233     def collapse(self, path, collapse=True):
   226         node = self._model.find(path)
   234         node = self._model.find(path)
   227         self.collapse_node(node, collapse)
   235         self.collapse_node(node, collapse)
   228 
   236 
   229     def collapse_node(self, node, collapse=True):
   237     def collapse_node(self, node, collapse=True):
   230         if collapse:
   238         if collapse:
   231             if not node in self.collapsed and len(node.children) > 0:
   239             if not node in self.collapsed_nodes and len(node.children) > 0:
   232                 self.collapsed.append(node)
   240                 self.collapsed_nodes.append(node)
   233                 self.emit('collapse', node)
   241                 self.emit('collapse', node)
   234         else:
   242         else:
   235             try:
   243             try:
   236                 self.collapsed.remove(node)
   244                 self.collapsed_nodes.remove(node)
   237                 self.emit('expand', node)
   245                 self.emit('expand', node)
   238             except ValueError:
   246             except ValueError:
   239                 pass
   247                 pass
   240         self._update_sizereq()
   248         self._update_sizereq()
   241 
   249 
   260             else:
   268             else:
   261                 head.append(ev.driver.unigraph.LLCORNER)
   269                 head.append(ev.driver.unigraph.LLCORNER)
   262                 lines &= ~(1 << level-1)
   270                 lines &= ~(1 << level-1)
   263             # draw lines and titles
   271             # draw lines and titles
   264             head = ''.join(head)
   272             head = ''.join(head)
   265             if node in self.collapsed:
   273             if node in self.collapsed_nodes:
   266                 sep = '+'
   274                 sep = '+'
   267             else:
   275             else:
   268                 sep = ' '
   276                 sep = ' '
   269             ev.driver.puts(ev.x, y, head + sep + str(node))
   277             ev.driver.puts(ev.x, y, head + sep + str(node))
   270             if node is self.cnode:
   278             if node is self.cursor_node:
   271                 ev.driver.pushcolor('active')
   279                 ev.driver.pushcolor('active')
   272                 ev.driver.puts(ev.x + len(head), y, sep + str(node) + ' ')
   280                 ev.driver.puts(ev.x + len(head), y, sep + str(node) + ' ')
   273                 ev.driver.popcolor()
   281                 ev.driver.popcolor()
   274             y += 1
   282             y += 1
   275 
   283 
   289         # previous sibling
   297         # previous sibling
   290         parent = node.parent
   298         parent = node.parent
   291         i = parent.children.index(node)
   299         i = parent.children.index(node)
   292         if i > 0:
   300         if i > 0:
   293             node = parent.children[i-1]
   301             node = parent.children[i-1]
   294             while node not in self.collapsed and len(node.children) > 0:
   302             while node not in self.collapsed_nodes and len(node.children) > 0:
   295                 node = node.children[-1]
   303                 node = node.children[-1]
   296             return node
   304             return node
   297         else:
   305         else:
   298             if parent.parent is None:
   306             if parent.parent is None:
   299                 return None
   307                 return None
   300             return parent
   308             return parent
   301 
   309 
   302     def next_node(self, node):
   310     def next_node(self, node):
   303         if node in self.collapsed or len(node.children) == 0:
   311         if node in self.collapsed_nodes or len(node.children) == 0:
   304             # next sibling
   312             # next sibling
   305             parent = node.parent
   313             parent = node.parent
   306             while parent is not None:
   314             while parent is not None:
   307                 i = parent.children.index(node)
   315                 i = parent.children.index(node)
   308                 try:
   316                 try:
   313             return None
   321             return None
   314         else:
   322         else:
   315             return node.children[0]
   323             return node.children[0]
   316 
   324 
   317     def move_up(self):
   325     def move_up(self):
   318         prev = self.prev_node(self.cnode)
   326         prev = self.prev_node(self.cursor_node)
   319         if prev is not None:
   327         if prev is not None:
   320             self.cnode = prev
   328             self.cursor_node = prev
   321             return True
   329             return True
   322         return False
   330         return False
   323 
   331 
   324     def move_down(self):
   332     def move_down(self):
   325         node = self.next_node(self.cnode)
   333         node = self.next_node(self.cursor_node)
   326         if node is not None:
   334         if node is not None:
   327             self.cnode = node
   335             self.cursor_node = node
   328             return True
   336             return True
   329         return False
   337         return False
   330 
   338 
   331     def move_left(self):
   339     def move_left(self):
   332         self.collapse_node(self.cnode, True)
   340         self.collapse_node(self.cursor_node, True)
   333 
   341 
   334     def move_right(self):
   342     def move_right(self):
   335         self.collapse_node(self.cnode, False)
   343         self.collapse_node(self.cursor_node, False)
   336 
   344 
   337     def _update_sizereq(self):
   345     def _update_sizereq(self):
   338         height = 0
   346         height = 0
   339         for num, _ in enumerate(self, start=1):
   347         for num, _ in enumerate(self, start=1):
   340             height = num
   348             height = num
   341         self.sizereq.h = height
   349         self.sizereq.h = height
   342 
   350 
       
   351     def _update_spot(self):
       
   352         """Update spot to current position of cursor node."""
       
   353         for num, (_level, _index, _count, node) in enumerate(self):
       
   354             if node is self.cursor_node:
       
   355                 self._spot.x = _level * 2
       
   356                 self._spot.y = num
       
   357         self.emit('spotmove')
       
   358