Ajout du GUI
This commit is contained in:
665
kivy/uix/treeview.py
Normal file
665
kivy/uix/treeview.py
Normal file
@@ -0,0 +1,665 @@
|
||||
'''
|
||||
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.
|
||||
'''
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, \
|
||||
AliasProperty, NumericProperty, ReferenceListProperty, ColorProperty
|
||||
|
||||
|
||||
class TreeViewException(Exception):
|
||||
'''Exception for errors in the :class:`TreeView`.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
class TreeViewNode(object):
|
||||
'''TreeViewNode class, used to build a node class for a TreeView object.
|
||||
'''
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if self.__class__ is TreeViewNode:
|
||||
raise TreeViewException('You cannot use directly TreeViewNode.')
|
||||
super(TreeViewNode, self).__init__(**kwargs)
|
||||
|
||||
is_leaf = BooleanProperty(True)
|
||||
'''Boolean to indicate whether this node is a leaf or not. Used to adjust
|
||||
the graphical representation.
|
||||
|
||||
:attr:`is_leaf` is a :class:`~kivy.properties.BooleanProperty` and defaults
|
||||
to True. It is automatically set to False when child is added.
|
||||
'''
|
||||
|
||||
is_open = BooleanProperty(False)
|
||||
'''Boolean to indicate whether this node is opened or not, in case there
|
||||
are child nodes. This is used to adjust the graphical representation.
|
||||
|
||||
.. warning::
|
||||
|
||||
This property is automatically set by the :class:`TreeView`. You can
|
||||
read but not write it.
|
||||
|
||||
:attr:`is_open` is a :class:`~kivy.properties.BooleanProperty` and defaults
|
||||
to False.
|
||||
'''
|
||||
|
||||
is_loaded = BooleanProperty(False)
|
||||
'''Boolean to indicate whether this node is already loaded or not. This
|
||||
property is used only if the :class:`TreeView` uses asynchronous loading.
|
||||
|
||||
:attr:`is_loaded` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
is_selected = BooleanProperty(False)
|
||||
'''Boolean to indicate whether this node is selected or not. This is used
|
||||
adjust the graphical representation.
|
||||
|
||||
.. warning::
|
||||
|
||||
This property is automatically set by the :class:`TreeView`. You can
|
||||
read but not write it.
|
||||
|
||||
:attr:`is_selected` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
no_selection = BooleanProperty(False)
|
||||
'''Boolean used to indicate whether selection of the node is allowed or
|
||||
not.
|
||||
|
||||
:attr:`no_selection` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
nodes = ListProperty([])
|
||||
'''List of nodes. The nodes list is different than the children list. A
|
||||
node in the nodes list represents a node on the tree. An item in the
|
||||
children list represents the widget associated with the node.
|
||||
|
||||
.. warning::
|
||||
|
||||
This property is automatically set by the :class:`TreeView`. You can
|
||||
read but not write it.
|
||||
|
||||
:attr:`nodes` is a :class:`~kivy.properties.ListProperty` and defaults to
|
||||
[].
|
||||
'''
|
||||
|
||||
parent_node = ObjectProperty(None, allownone=True)
|
||||
'''Parent node. This attribute is needed because the :attr:`parent` can be
|
||||
None when the node is not displayed.
|
||||
|
||||
.. versionadded:: 1.0.7
|
||||
|
||||
:attr:`parent_node` is an :class:`~kivy.properties.ObjectProperty` and
|
||||
defaults to None.
|
||||
'''
|
||||
|
||||
level = NumericProperty(-1)
|
||||
'''Level of the node.
|
||||
|
||||
:attr:`level` is a :class:`~kivy.properties.NumericProperty` and defaults
|
||||
to -1.
|
||||
'''
|
||||
|
||||
color_selected = ColorProperty([.3, .3, .3, 1.])
|
||||
'''Background color of the node when the node is selected.
|
||||
|
||||
:attr:`color_selected` is a :class:`~kivy.properties.ColorProperty` and
|
||||
defaults to [.1, .1, .1, 1].
|
||||
|
||||
.. versionchanged:: 2.0.0
|
||||
Changed from :class:`~kivy.properties.ListProperty` to
|
||||
:class:`~kivy.properties.ColorProperty`.
|
||||
'''
|
||||
|
||||
odd = BooleanProperty(False)
|
||||
'''
|
||||
This property is set by the TreeView widget automatically and is read-only.
|
||||
|
||||
:attr:`odd` is a :class:`~kivy.properties.BooleanProperty` and defaults to
|
||||
False.
|
||||
'''
|
||||
|
||||
odd_color = ColorProperty([1., 1., 1., .0])
|
||||
'''Background color of odd nodes when the node is not selected.
|
||||
|
||||
:attr:`odd_color` is a :class:`~kivy.properties.ColorProperty` and defaults
|
||||
to [1., 1., 1., 0.].
|
||||
|
||||
.. versionchanged:: 2.0.0
|
||||
Changed from :class:`~kivy.properties.ListProperty` to
|
||||
:class:`~kivy.properties.ColorProperty`.
|
||||
'''
|
||||
|
||||
even_color = ColorProperty([0.5, 0.5, 0.5, 0.1])
|
||||
'''Background color of even nodes when the node is not selected.
|
||||
|
||||
:attr:`bg_color` is a :class:`~kivy.properties.ColorProperty` and defaults
|
||||
to [.5, .5, .5, .1].
|
||||
|
||||
.. versionchanged:: 2.0.0
|
||||
Changed from :class:`~kivy.properties.ListProperty` to
|
||||
:class:`~kivy.properties.ColorProperty`.
|
||||
'''
|
||||
|
||||
|
||||
class TreeViewLabel(Label, TreeViewNode):
|
||||
'''Combines 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.
|
||||
'''
|
||||
|
||||
|
||||
class TreeView(Widget):
|
||||
'''TreeView 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
|
||||
'''
|
||||
|
||||
__events__ = ('on_node_expand', 'on_node_collapse')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._trigger_layout = Clock.create_trigger(self._do_layout, -1)
|
||||
super(TreeView, self).__init__(**kwargs)
|
||||
tvlabel = TreeViewLabel(text='Root', is_open=True, level=0)
|
||||
for key, value in self.root_options.items():
|
||||
setattr(tvlabel, key, value)
|
||||
self._root = self.add_node(tvlabel, None)
|
||||
|
||||
trigger = self._trigger_layout
|
||||
fbind = self.fbind
|
||||
fbind('pos', trigger)
|
||||
fbind('size', trigger)
|
||||
fbind('indent_level', trigger)
|
||||
fbind('indent_start', trigger)
|
||||
trigger()
|
||||
|
||||
def add_node(self, node, parent=None):
|
||||
'''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`.
|
||||
'''
|
||||
# check if the widget is "ok" for a node
|
||||
if not isinstance(node, TreeViewNode):
|
||||
raise TreeViewException(
|
||||
'The node must be a subclass of TreeViewNode')
|
||||
# create node
|
||||
if parent is None and self._root:
|
||||
parent = self._root
|
||||
if parent:
|
||||
parent.is_leaf = False
|
||||
parent.nodes.append(node)
|
||||
node.parent_node = parent
|
||||
node.level = parent.level + 1
|
||||
node.fbind('size', self._trigger_layout)
|
||||
self._trigger_layout()
|
||||
return node
|
||||
|
||||
def remove_node(self, node):
|
||||
'''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.
|
||||
'''
|
||||
# check if the widget is "ok" for a node
|
||||
if not isinstance(node, TreeViewNode):
|
||||
raise TreeViewException(
|
||||
'The node must be a subclass of TreeViewNode')
|
||||
parent = node.parent_node
|
||||
if parent is not None:
|
||||
if node == self._selected_node:
|
||||
node.is_selected = False
|
||||
self._selected_node = None
|
||||
nodes = parent.nodes
|
||||
if node in nodes:
|
||||
nodes.remove(node)
|
||||
parent.is_leaf = not bool(len(nodes))
|
||||
node.parent_node = None
|
||||
node.funbind('size', self._trigger_layout)
|
||||
self._trigger_layout()
|
||||
|
||||
def on_node_expand(self, node):
|
||||
pass
|
||||
|
||||
def on_node_collapse(self, node):
|
||||
pass
|
||||
|
||||
def select_node(self, node):
|
||||
'''Select a node in the tree.
|
||||
'''
|
||||
if node.no_selection:
|
||||
return
|
||||
if self._selected_node:
|
||||
self._selected_node.is_selected = False
|
||||
node.is_selected = True
|
||||
self._selected_node = node
|
||||
|
||||
def deselect_node(self, *args):
|
||||
'''Deselect any selected node.
|
||||
|
||||
.. versionadded:: 1.10.0
|
||||
'''
|
||||
if self._selected_node:
|
||||
self._selected_node.is_selected = False
|
||||
self._selected_node = None
|
||||
|
||||
def toggle_node(self, node):
|
||||
'''Toggle the state of the node (open/collapsed).
|
||||
'''
|
||||
node.is_open = not node.is_open
|
||||
if node.is_open:
|
||||
if self.load_func and not node.is_loaded:
|
||||
self._do_node_load(node)
|
||||
self.dispatch('on_node_expand', node)
|
||||
else:
|
||||
self.dispatch('on_node_collapse', node)
|
||||
self._trigger_layout()
|
||||
|
||||
def get_node_at_pos(self, pos):
|
||||
'''Get the node at the position (x, y).
|
||||
'''
|
||||
x, y = pos
|
||||
for node in self.iterate_open_nodes(self.root):
|
||||
if self.x <= x <= self.right and \
|
||||
node.y <= y <= node.top:
|
||||
return node
|
||||
|
||||
def iterate_open_nodes(self, node=None):
|
||||
'''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)
|
||||
|
||||
'''
|
||||
if not node:
|
||||
node = self.root
|
||||
if self.hide_root and node is self.root:
|
||||
pass
|
||||
else:
|
||||
yield node
|
||||
if not node.is_open:
|
||||
return
|
||||
f = self.iterate_open_nodes
|
||||
for cnode in node.nodes:
|
||||
for ynode in f(cnode):
|
||||
yield ynode
|
||||
|
||||
def iterate_all_nodes(self, node=None):
|
||||
'''Generator to iterate over all nodes from `node` and down whether
|
||||
expanded or not. If `node` is `None`, the generator start with
|
||||
:attr:`root`.
|
||||
'''
|
||||
if not node:
|
||||
node = self.root
|
||||
yield node
|
||||
f = self.iterate_all_nodes
|
||||
for cnode in node.nodes:
|
||||
for ynode in f(cnode):
|
||||
yield ynode
|
||||
|
||||
#
|
||||
# Private
|
||||
#
|
||||
def on_load_func(self, instance, value):
|
||||
if value:
|
||||
Clock.schedule_once(self._do_initial_load)
|
||||
|
||||
def _do_initial_load(self, *largs):
|
||||
if not self.load_func:
|
||||
return
|
||||
self._do_node_load(None)
|
||||
|
||||
def _do_node_load(self, node):
|
||||
gen = self.load_func(self, node)
|
||||
if node:
|
||||
node.is_loaded = True
|
||||
if not gen:
|
||||
return
|
||||
for cnode in gen:
|
||||
self.add_node(cnode, node)
|
||||
|
||||
def on_root_options(self, instance, value):
|
||||
if not self.root:
|
||||
return
|
||||
for key, value in value.items():
|
||||
setattr(self.root, key, value)
|
||||
|
||||
def _do_layout(self, *largs):
|
||||
self.clear_widgets()
|
||||
# display only the one who are is_open
|
||||
self._do_open_node(self.root)
|
||||
# now do layout
|
||||
self._do_layout_node(self.root, 0, self.top)
|
||||
# now iterate for calculating minimum size
|
||||
min_width = min_height = 0
|
||||
for count, node in enumerate(self.iterate_open_nodes(self.root)):
|
||||
node.odd = False if count % 2 else True
|
||||
min_width = max(min_width, node.right - self.x)
|
||||
min_height += node.height
|
||||
self.minimum_size = (min_width, min_height)
|
||||
|
||||
def _do_open_node(self, node):
|
||||
if self.hide_root and node is self.root:
|
||||
height = 0
|
||||
else:
|
||||
self.add_widget(node)
|
||||
height = node.height
|
||||
if not node.is_open:
|
||||
return height
|
||||
for cnode in node.nodes:
|
||||
height += self._do_open_node(cnode)
|
||||
return height
|
||||
|
||||
def _do_layout_node(self, node, level, y):
|
||||
if self.hide_root and node is self.root:
|
||||
level -= 1
|
||||
else:
|
||||
node.x = self.x + self.indent_start + level * self.indent_level
|
||||
node.top = y
|
||||
if node.size_hint_x:
|
||||
node.width = (self.width - (node.x - self.x)) \
|
||||
* node.size_hint_x
|
||||
y -= node.height
|
||||
if not node.is_open:
|
||||
return y
|
||||
for cnode in node.nodes:
|
||||
y = self._do_layout_node(cnode, level + 1, y)
|
||||
return y
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
node = self.get_node_at_pos(touch.pos)
|
||||
if not node:
|
||||
return
|
||||
if node.disabled:
|
||||
return
|
||||
# toggle node or selection ?
|
||||
if node.x - self.indent_start <= touch.x < node.x:
|
||||
self.toggle_node(node)
|
||||
elif node.x <= touch.x:
|
||||
self.select_node(node)
|
||||
node.dispatch('on_touch_down', touch)
|
||||
return True
|
||||
|
||||
#
|
||||
# Private properties
|
||||
#
|
||||
_root = ObjectProperty(None)
|
||||
|
||||
_selected_node = ObjectProperty(None, allownone=True)
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
minimum_width = NumericProperty(0)
|
||||
'''Minimum width needed to contain all children.
|
||||
|
||||
.. versionadded:: 1.0.9
|
||||
|
||||
:attr:`minimum_width` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 0.
|
||||
'''
|
||||
|
||||
minimum_height = NumericProperty(0)
|
||||
'''Minimum height needed to contain all children.
|
||||
|
||||
.. versionadded:: 1.0.9
|
||||
|
||||
:attr:`minimum_height` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 0.
|
||||
'''
|
||||
|
||||
minimum_size = ReferenceListProperty(minimum_width, minimum_height)
|
||||
'''Minimum size needed to contain all children.
|
||||
|
||||
.. versionadded:: 1.0.9
|
||||
|
||||
:attr:`minimum_size` is a :class:`~kivy.properties.ReferenceListProperty`
|
||||
of (:attr:`minimum_width`, :attr:`minimum_height`) properties.
|
||||
'''
|
||||
|
||||
indent_level = NumericProperty('16dp')
|
||||
'''Width used for the indentation of each level except the first level.
|
||||
|
||||
Computation of indent for each level of the tree is::
|
||||
|
||||
indent = indent_start + level * indent_level
|
||||
|
||||
:attr:`indent_level` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 16.
|
||||
'''
|
||||
|
||||
indent_start = NumericProperty('24dp')
|
||||
'''Indentation width of the level 0 / root node. This is mostly the initial
|
||||
size to accommodate a tree icon (collapsed / expanded). See
|
||||
:attr:`indent_level` for more information about the computation of level
|
||||
indentation.
|
||||
|
||||
:attr:`indent_start` is a :class:`~kivy.properties.NumericProperty` and
|
||||
defaults to 24.
|
||||
'''
|
||||
|
||||
hide_root = BooleanProperty(False)
|
||||
'''Use this property to show/hide the initial root node. If True, the root
|
||||
node will be appear as a closed node.
|
||||
|
||||
:attr:`hide_root` is a :class:`~kivy.properties.BooleanProperty` and
|
||||
defaults to False.
|
||||
'''
|
||||
|
||||
def get_selected_node(self):
|
||||
return self._selected_node
|
||||
|
||||
selected_node = AliasProperty(get_selected_node, None,
|
||||
bind=('_selected_node', ))
|
||||
'''Node selected by :meth:`TreeView.select_node` or by touch.
|
||||
|
||||
:attr:`selected_node` is a :class:`~kivy.properties.AliasProperty` and
|
||||
defaults to None. It is read-only.
|
||||
'''
|
||||
|
||||
def get_root(self):
|
||||
return self._root
|
||||
|
||||
root = AliasProperty(get_root, None, bind=('_root', ))
|
||||
'''Root node.
|
||||
|
||||
By default, the root node widget is a :class:`TreeViewLabel` with text
|
||||
'Root'. If you want to change the default options passed to the widget
|
||||
creation, use the :attr:`root_options` property::
|
||||
|
||||
treeview = TreeView(root_options={
|
||||
'text': 'Root directory',
|
||||
'font_size': 15})
|
||||
|
||||
:attr:`root_options` will change the properties of the
|
||||
:class:`TreeViewLabel` instance. However, you cannot change the class used
|
||||
for root node yet.
|
||||
|
||||
:attr:`root` is an :class:`~kivy.properties.AliasProperty` and defaults to
|
||||
None. It is read-only. However, the content of the widget can be changed.
|
||||
'''
|
||||
|
||||
root_options = ObjectProperty({})
|
||||
'''Default root options to pass for root widget. See :attr:`root` property
|
||||
for more information about the usage of root_options.
|
||||
|
||||
:attr:`root_options` is an :class:`~kivy.properties.ObjectProperty` and
|
||||
defaults to {}.
|
||||
'''
|
||||
|
||||
load_func = ObjectProperty(None)
|
||||
'''Callback to use for asynchronous loading. If set, asynchronous loading
|
||||
will be automatically done. The callback must act as a Python generator
|
||||
function, using yield to send data back to the treeview.
|
||||
|
||||
The callback should be in the format::
|
||||
|
||||
def callback(treeview, node):
|
||||
for name in ('Item 1', 'Item 2'):
|
||||
yield TreeViewLabel(text=name)
|
||||
|
||||
:attr:`load_func` is a :class:`~kivy.properties.ObjectProperty` and
|
||||
defaults to None.
|
||||
'''
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.app import App
|
||||
|
||||
class TestApp(App):
|
||||
|
||||
def build(self):
|
||||
tv = TreeView(hide_root=True)
|
||||
add = tv.add_node
|
||||
root = add(TreeViewLabel(text='Level 1, entry 1', is_open=True))
|
||||
for x in range(5):
|
||||
add(TreeViewLabel(text='Element %d' % x), root)
|
||||
root2 = add(TreeViewLabel(text='Level 1, entry 2', is_open=False))
|
||||
for x in range(24):
|
||||
add(TreeViewLabel(text='Element %d' % x), root2)
|
||||
for x in range(5):
|
||||
add(TreeViewLabel(text='Element %d' % x), root)
|
||||
root2 = add(TreeViewLabel(text='Element childs 2', is_open=False),
|
||||
root)
|
||||
for x in range(24):
|
||||
add(TreeViewLabel(text='Element %d' % x), root2)
|
||||
return tv
|
||||
TestApp().run()
|
||||
Reference in New Issue
Block a user