U
    PeT                     @   s   d Z ddlmZ ddlmZ ddlmZ ddlmZm	Z	m
Z
mZmZmZmZ G dd deZG dd	 d	eZG d
d deeZG dd deZedkrddlmZ G dd deZe   dS )a  
Tree View
=========

.. image:: images/treeview.png
    :align: right

.. versionadded:: 1.0.4


:class:`TreeView` is a widget used to represent a tree structure. It is
currently very basic, supporting a minimal feature set.

Introduction
------------

A :class:`TreeView` is populated with :class:`TreeViewNode` instances, but you
cannot use a :class:`TreeViewNode` directly. You must combine it with another
widget, such as :class:`~kivy.uix.label.Label`,
:class:`~kivy.uix.button.Button` or even your own widget. The TreeView
always creates a default root node, based on :class:`TreeViewLabel`.

:class:`TreeViewNode` is a class object containing needed properties for
serving as a tree node. Extend :class:`TreeViewNode` to create custom node
types for use with a :class:`TreeView`.

For constructing your own subclass, follow the pattern of TreeViewLabel which
combines a Label and a TreeViewNode, producing a :class:`TreeViewLabel` for
direct use in a TreeView instance.

To use the TreeViewLabel class, you could create two nodes directly attached
to root::

    tv = TreeView()
    tv.add_node(TreeViewLabel(text='My first item'))
    tv.add_node(TreeViewLabel(text='My second item'))

Or, create two nodes attached to a first::

    tv = TreeView()
    n1 = tv.add_node(TreeViewLabel(text='Item 1'))
    tv.add_node(TreeViewLabel(text='SubItem 1'), n1)
    tv.add_node(TreeViewLabel(text='SubItem 2'), n1)

If you have a large tree structure, perhaps you would need a utility function
to populate the tree view::

    def populate_tree_view(tree_view, parent, node):
        if parent is None:
            tree_node = tree_view.add_node(TreeViewLabel(text=node['node_id'],
                                                         is_open=True))
        else:
            tree_node = tree_view.add_node(TreeViewLabel(text=node['node_id'],
                                                         is_open=True), parent)

        for child_node in node['children']:
            populate_tree_view(tree_view, tree_node, child_node)


    tree = {'node_id': '1',
            'children': [{'node_id': '1.1',
                          'children': [{'node_id': '1.1.1',
                                        'children': [{'node_id': '1.1.1.1',
                                                      'children': []}]},
                                       {'node_id': '1.1.2',
                                        'children': []},
                                       {'node_id': '1.1.3',
                                        'children': []}]},
                          {'node_id': '1.2',
                           'children': []}]}


    class TreeWidget(FloatLayout):
        def __init__(self, **kwargs):
            super(TreeWidget, self).__init__(**kwargs)

            tv = TreeView(root_options=dict(text='Tree One'),
                          hide_root=False,
                          indent_level=4)

            populate_tree_view(tv, None, tree)

            self.add_widget(tv)

The root widget in the tree view is opened by default and has text set as
'Root'. If you want to change that, you can use the
:attr:`TreeView.root_options`
property. This will pass options to the root widget::

    tv = TreeView(root_options=dict(text='My root label'))


Creating Your Own Node Widget
-----------------------------

For a button node type, combine a :class:`~kivy.uix.button.Button` and a
:class:`TreeViewNode` as follows::

    class TreeViewButton(Button, TreeViewNode):
        pass

You must know that, for a given node, only the
:attr:`~kivy.uix.widget.Widget.size_hint_x` will be honored. The allocated
width for the node will depend of the current width of the TreeView and the
level of the node. For example, if a node is at level 4, the width
allocated will be:

    treeview.width - treeview.indent_start - treeview.indent_level * node.level

You might have some trouble with that. It is the developer's responsibility to
correctly handle adapting the graphical representation nodes, if needed.
    )Clock)Label)Widget)BooleanPropertyListPropertyObjectPropertyAliasPropertyNumericPropertyReferenceListPropertyColorPropertyc                   @   s   e Zd ZdZdS )TreeViewExceptionz3Exception for errors in the :class:`TreeView`.
    N__name__
__module____qualname____doc__ r   r   5/tmp/pip-unpacked-wheel-xzebddm3/kivy/uix/treeview.pyr   y   s   r   c                       s   e Zd ZdZ fddZedZedZedZedZ	edZ
eg ZedddZedZed	d	d	d
gZedZed
d
d
dgZeddddgZ  ZS )TreeViewNodezJTreeViewNode class, used to build a node class for a TreeView object.
    c                    s(   | j tkrtdtt| jf | d S )Nz%You cannot use directly TreeViewNode.)	__class__r   r   super__init__)selfkwargsr   r   r   r      s    
zTreeViewNode.__init__TFNZ	allownoneg333333?g      ?g        g      ?g?)r   r   r   r   r   r   is_leafis_open	is_loadedis_selectedno_selectionr   nodesr   parent_noder	   levelr   Zcolor_selectedoddZ	odd_colorZ
even_color__classcell__r   r   r   r   r      s   
r   c                   @   s   e Zd ZdZdS )TreeViewLabelzCombines a :class:`~kivy.uix.label.Label` and a :class:`TreeViewNode` to
    create a :class:`TreeViewLabel` that can be used as a text node in the
    tree.

    See module documentation for more information.
    Nr   r   r   r   r   r'     s   r'   c                       s<  e Zd ZdZdZ fddZd7ddZdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Zd8ddZd9ddZdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) ZedZedd*d+Zed,Zed,ZeeeZed-Z ed.Z!e"d/Z#d0d1 Z$e%e$dd2d3Z&d4d5 Z'e%e'dd6d3Z(ei Z)edZ*  Z+S ):TreeViewzTreeView class. See module documentation for more information.

    :Events:
        `on_node_expand`: (node, )
            Fired when a node is being expanded
        `on_node_collapse`: (node, )
            Fired when a node is being collapsed
    )on_node_expandon_node_collapsec                    s   t | jd| _tt| jf | tdddd}| j	 D ]\}}t
||| q:| |d | _| j}| j}|d| |d| |d| |d	| |  d S )
Nr   ZRootTr   )textr   r$   possizeindent_levelindent_start)r   Zcreate_trigger
_do_layout_trigger_layoutr   r(   r   r'   root_optionsitemssetattradd_node_rootfbind)r   r   ZtvlabelkeyvalueZtriggerr7   r   r   r   r     s    



zTreeView.__init__Nc                 C   sh   t |tstd|dkr&| jr&| j}|rNd|_|j| ||_|jd |_|	d| j
 | 
  |S )a  Add a new node to the tree.

        :Parameters:
            `node`: instance of a :class:`TreeViewNode`
                Node to add into the tree
            `parent`: instance of a :class:`TreeViewNode`, defaults to None
                Parent node to attach the new node. If `None`, it is added to
                the :attr:`root` node.

        :returns:
            the node `node`.
        +The node must be a subclass of TreeViewNodeNF   r-   )
isinstancer   r   r6   r   r"   appendr#   r$   r7   r1   )r   nodeparentr   r   r   r5   (  s    
zTreeView.add_nodec                 C   s~   t |tstd|j}|dk	rz|| jkr6d|_d| _|j}||krN|| tt	| |_
d|_|d| j |   dS )a  Removes a node from the tree.

        .. versionadded:: 1.0.7

        :Parameters:
            `node`: instance of a :class:`TreeViewNode`
                Node to remove from the tree. If `node` is :attr:`root`, it is
                not removed.
        r:   NFr-   )r<   r   r   r#   _selected_noder    r"   removeboollenr   Zfunbindr1   )r   r>   r?   r"   r   r   r   remove_nodeE  s     


zTreeView.remove_nodec                 C   s   d S Nr   r   r>   r   r   r   r)   `  s    zTreeView.on_node_expandc                 C   s   d S rE   r   rF   r   r   r   r*   c  s    zTreeView.on_node_collapsec                 C   s(   |j r
dS | jrd| j_d|_|| _dS )z#Select a node in the tree.
        NFT)r!   r@   r    rF   r   r   r   select_nodef  s    zTreeView.select_nodec                 G   s   | j rd| j _d| _ dS )zFDeselect any selected node.

        .. versionadded:: 1.10.0
        FN)r@   r    )r   argsr   r   r   deselect_nodep  s    zTreeView.deselect_nodec                 C   sL   |j  |_ |j r4| jr&|js&| | | d| n| d| |   dS )z7Toggle the state of the node (open/collapsed).
        r)   r*   N)r   	load_funcr   _do_node_loaddispatchr1   rF   r   r   r   toggle_nodey  s    

zTreeView.toggle_nodec                 C   s^   |\}}|  | jD ]D}| j|  kr0| jkrn q|j|  krL|jkrn q|  S qdS )z-Get the node at the position (x, y).
        N)iterate_open_nodesrootxrightytop)r   r,   rP   rR   r>   r   r   r   get_node_at_pos  s     
zTreeView.get_node_at_posc                 c   sV   |s
| j }| jr|| j krn|V  |js,dS | j}|jD ]}||D ]
}|V  qDq8dS )a`  Generator to iterate over all the expended nodes starting from
        `node` and down. If `node` is `None`, the generator start with
        :attr:`root`.

        To get all the open nodes::

            treeview = TreeView()
            # ... add nodes ...
            for node in treeview.iterate_open_nodes():
                print(node)

        N)rO   	hide_rootr   rN   r"   r   r>   fcnodeZynoder   r   r   rN     s    
zTreeView.iterate_open_nodesc                 c   s:   |s
| j }|V  | j}|jD ]}||D ]
}|V  q(qdS )zGenerator to iterate over all nodes from `node` and down whether
        expanded or not. If `node` is `None`, the generator start with
        :attr:`root`.
        N)rO   iterate_all_nodesr"   rV   r   r   r   rY     s    
zTreeView.iterate_all_nodesc                 C   s   |rt | j d S rE   )r   Zschedule_once_do_initial_load)r   instancer9   r   r   r   on_load_func  s    zTreeView.on_load_funcc                 G   s   | j s
d S | d  d S rE   )rJ   rK   )r   largsr   r   r   rZ     s    zTreeView._do_initial_loadc                 C   s8   |  | |}|rd|_|sd S |D ]}| || q"d S )NT)rJ   r   r5   )r   r>   genrX   r   r   r   rK     s    zTreeView._do_node_loadc                 C   s.   | j s
d S | D ]\}}t| j || qd S rE   )rO   r3   r4   )r   r[   r9   r8   r   r   r   on_root_options  s    zTreeView.on_root_optionsc                 G   s   |    | | j | | jd| j d }}t| | jD ]6\}}|d rRdnd|_t||j	| j
 }||j7 }q>||f| _d S )Nr      FT)Zclear_widgets_do_open_noderO   _do_layout_noderS   	enumeraterN   r%   maxrQ   rP   heightminimum_size)r   r]   Z	min_widthZ
min_heightcountr>   r   r   r   r0     s    zTreeView._do_layoutc                 C   sN   | j r|| jkrd}n| | |j}|js0|S |jD ]}|| |7 }q6|S )Nr   )rU   rO   Z
add_widgetre   r   r"   ra   )r   r>   re   rX   r   r   r   ra     s    

zTreeView._do_open_nodec                 C   s   | j r|| jkr|d8 }nR| j| j || j  |_||_|jrX| j|j| j  |j |_||j8 }|j	sl|S |j
D ]}| ||d |}qr|S )Nr;   )rU   rO   rP   r/   r.   rS   Zsize_hint_xwidthre   r   r"   rb   )r   r>   r$   rR   rX   r   r   r   rb     s    


zTreeView._do_layout_nodec                 C   st   |  |j}|sd S |jrd S |j| j |j  kr>|jk rNn n| | n"|j|jkrp| | |d| dS )Non_touch_downT)rT   r,   disabledrP   r/   rM   rG   rL   )r   touchr>   r   r   r   ri     s    $
zTreeView.on_touch_downTr   r   Z16dpZ24dpFc                 C   s   | j S rE   r@   r   r   r   r   get_selected_nodeK  s    zTreeView.get_selected_noderl   )bindc                 C   s   | j S rE   r6   rm   r   r   r   get_rootV  s    zTreeView.get_rootrp   )N)N)N),r   r   r   r   Z
__events__r   r5   rD   r)   r*   rG   rI   rM   rT   rN   rY   r\   rZ   rK   r_   r0   ra   rb   ri   r   r6   r@   r	   Zminimum_widthZminimum_heightr
   rf   r.   r/   r   rU   rn   r   Zselected_noderq   rO   r2   rJ   r&   r   r   r   r   r(     sL   	

		

			
	
r(   __main__)Appc                   @   s   e Zd Zdd ZdS )TestAppc                 C   s   t dd}|j}|tddd}tdD ]}|td| d| q(|tdd	d}td
D ]}|td| d| qZtdD ]}|td| d| q||tdd	d|}td
D ]}|td| d| q|S )NT)rU   zLevel 1, entry 1)r+   r      z
Element %d)r+   zLevel 1, entry 2F   zElement childs 2)r(   r5   r'   range)r   tvaddrO   rP   root2r   r   r   build  s     
zTestApp.buildN)r   r   r   r{   r   r   r   r   rt     s   rt   N)r   Z
kivy.clockr   Zkivy.uix.labelr   Zkivy.uix.widgetr   Zkivy.propertiesr   r   r   r   r	   r
   r   	Exceptionr   objectr   r'   r(   r   Zkivy.apprs   rt   runr   r   r   r   <module>   s   q$ 	  z