396 lines
11 KiB
Python
396 lines
11 KiB
Python
|
"""
|
||
|
Components/Expansion Panel
|
||
|
==========================
|
||
|
|
||
|
.. seealso::
|
||
|
|
||
|
`Material Design spec, Expansion panel <https://material.io/archive/guidelines/components/expansion-panels.html#>`_
|
||
|
|
||
|
.. rubric:: Expansion panels contain creation flows and allow lightweight editing of an element.
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/expansion-panel.png
|
||
|
:align: center
|
||
|
|
||
|
Usage
|
||
|
-----
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
self.add_widget(
|
||
|
MDExpansionPanel(
|
||
|
icon="logo.png", # panel icon
|
||
|
content=Content(), # panel content
|
||
|
panel_cls=MDExpansionPanelOneLine(text="Secondary text"), # panel class
|
||
|
)
|
||
|
)
|
||
|
|
||
|
To use :class:`~MDExpansionPanel` you must pass one of the following classes
|
||
|
to the :attr:`~MDExpansionPanel.panel_cls` parameter:
|
||
|
|
||
|
- :class:`~MDExpansionPanelOneLine`
|
||
|
- :class:`~MDExpansionPanelTwoLine`
|
||
|
- :class:`~MDExpansionPanelThreeLine`
|
||
|
|
||
|
These classes are inherited from the following classes:
|
||
|
|
||
|
- :class:`~kivymd.uix.list.OneLineAvatarIconListItem`
|
||
|
- :class:`~kivymd.uix.list.TwoLineAvatarIconListItem`
|
||
|
- :class:`~kivymd.uix.list.ThreeLineAvatarIconListItem`
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
self.root.ids.box.add_widget(
|
||
|
MDExpansionPanel(
|
||
|
icon="logo.png",
|
||
|
content=Content(),
|
||
|
panel_cls=MDExpansionPanelThreeLine(
|
||
|
text="Text",
|
||
|
secondary_text="Secondary text",
|
||
|
tertiary_text="Tertiary text",
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
Example
|
||
|
-------
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||
|
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelThreeLine
|
||
|
from kivymd import images_path
|
||
|
|
||
|
KV = '''
|
||
|
<Content>
|
||
|
adaptive_height: True
|
||
|
|
||
|
TwoLineIconListItem:
|
||
|
text: "(050)-123-45-67"
|
||
|
secondary_text: "Mobile"
|
||
|
|
||
|
IconLeftWidget:
|
||
|
icon: 'phone'
|
||
|
|
||
|
|
||
|
ScrollView:
|
||
|
|
||
|
MDGridLayout:
|
||
|
id: box
|
||
|
cols: 1
|
||
|
adaptive_height: True
|
||
|
'''
|
||
|
|
||
|
|
||
|
class Content(MDBoxLayout):
|
||
|
'''Custom content.'''
|
||
|
|
||
|
|
||
|
class Test(MDApp):
|
||
|
def build(self):
|
||
|
return Builder.load_string(KV)
|
||
|
|
||
|
def on_start(self):
|
||
|
for i in range(10):
|
||
|
self.root.ids.box.add_widget(
|
||
|
MDExpansionPanel(
|
||
|
icon=f"{images_path}kivymd.png",
|
||
|
content=Content(),
|
||
|
panel_cls=MDExpansionPanelThreeLine(
|
||
|
text="Text",
|
||
|
secondary_text="Secondary text",
|
||
|
tertiary_text="Tertiary text",
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
|
||
|
Test().run()
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/expansion-panel.gif
|
||
|
:align: center
|
||
|
|
||
|
Two events are available for :class:`~MDExpansionPanel`
|
||
|
------------------------------------------------------
|
||
|
|
||
|
- :attr:`~MDExpansionPanel.on_open`
|
||
|
- :attr:`~MDExpansionPanel.on_close`
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDExpansionPanel:
|
||
|
on_open: app.on_panel_open(args)
|
||
|
on_close: app.on_panel_close(args)
|
||
|
|
||
|
The user function takes one argument - the object of the panel:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
def on_panel_open(self, instance_panel):
|
||
|
print(instance_panel)
|
||
|
|
||
|
.. seealso:: `See Expansion panel example <https://github.com/kivymd/KivyMD/wiki/Components-Expansion-Panel>`_
|
||
|
|
||
|
`Expansion panel and MDCard <https://github.com/kivymd/KivyMD/wiki/Components-Expansion-Panel-and-MDCard>`_
|
||
|
"""
|
||
|
|
||
|
__all__ = (
|
||
|
"MDExpansionPanel",
|
||
|
"MDExpansionPanelOneLine",
|
||
|
"MDExpansionPanelTwoLine",
|
||
|
"MDExpansionPanelThreeLine",
|
||
|
)
|
||
|
|
||
|
from kivy.animation import Animation
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.properties import NumericProperty, ObjectProperty, StringProperty
|
||
|
from kivy.uix.relativelayout import RelativeLayout
|
||
|
from kivy.uix.widget import WidgetException
|
||
|
|
||
|
import kivymd.material_resources as m_res
|
||
|
from kivymd.icon_definitions import md_icons
|
||
|
from kivymd.uix.button import MDIconButton
|
||
|
from kivymd.uix.list import (
|
||
|
IconLeftWidget,
|
||
|
ImageLeftWidget,
|
||
|
IRightBodyTouch,
|
||
|
OneLineAvatarIconListItem,
|
||
|
ThreeLineAvatarIconListItem,
|
||
|
TwoLineAvatarIconListItem,
|
||
|
)
|
||
|
|
||
|
Builder.load_string(
|
||
|
"""
|
||
|
<MDExpansionChevronRight>:
|
||
|
icon: 'chevron-right'
|
||
|
disabled: True
|
||
|
|
||
|
canvas.before:
|
||
|
PushMatrix
|
||
|
Rotate:
|
||
|
angle: self._angle
|
||
|
axis: (0, 0, 1)
|
||
|
origin: self.center
|
||
|
canvas.after:
|
||
|
PopMatrix
|
||
|
|
||
|
|
||
|
<MDExpansionPanel>
|
||
|
size_hint_y: None
|
||
|
#height: dp(68)
|
||
|
"""
|
||
|
)
|
||
|
|
||
|
|
||
|
class MDExpansionChevronRight(IRightBodyTouch, MDIconButton):
|
||
|
"""Chevron icon on the right panel."""
|
||
|
|
||
|
_angle = NumericProperty(0)
|
||
|
|
||
|
|
||
|
class MDExpansionPanelOneLine(OneLineAvatarIconListItem):
|
||
|
"""Single line panel."""
|
||
|
|
||
|
|
||
|
class MDExpansionPanelTwoLine(TwoLineAvatarIconListItem):
|
||
|
"""Two-line panel."""
|
||
|
|
||
|
|
||
|
class MDExpansionPanelThreeLine(ThreeLineAvatarIconListItem):
|
||
|
"""Three-line panel."""
|
||
|
|
||
|
|
||
|
class MDExpansionPanel(RelativeLayout):
|
||
|
"""
|
||
|
:Events:
|
||
|
:attr:`on_open`
|
||
|
Called when a panel is opened.
|
||
|
:attr:`on_close`
|
||
|
Called when a panel is closed.
|
||
|
"""
|
||
|
|
||
|
content = ObjectProperty()
|
||
|
"""Content of panel. Must be `Kivy` widget.
|
||
|
|
||
|
:attr:`content` is an :class:`~kivy.properties.ObjectProperty`
|
||
|
and defaults to `None`.
|
||
|
"""
|
||
|
|
||
|
icon = StringProperty()
|
||
|
"""Icon of panel.
|
||
|
|
||
|
Icon Should be either be a path to an image or
|
||
|
a logo name in :class:`~kivymd.icon_definitions.md_icons`
|
||
|
|
||
|
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||
|
and defaults to `''`.
|
||
|
"""
|
||
|
|
||
|
opening_transition = StringProperty("out_cubic")
|
||
|
"""
|
||
|
The name of the animation transition type to use when animating to
|
||
|
the :attr:`state` `'open'`.
|
||
|
|
||
|
:attr:`opening_transition` is a :class:`~kivy.properties.StringProperty`
|
||
|
and defaults to `'out_cubic'`.
|
||
|
"""
|
||
|
|
||
|
opening_time = NumericProperty(0.2)
|
||
|
"""
|
||
|
The time taken for the panel to slide to the :attr:`state` `'open'`.
|
||
|
|
||
|
:attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
|
||
|
and defaults to `0.2`.
|
||
|
"""
|
||
|
|
||
|
closing_transition = StringProperty("out_sine")
|
||
|
"""The name of the animation transition type to use when animating to
|
||
|
the :attr:`state` 'close'.
|
||
|
|
||
|
:attr:`closing_transition` is a :class:`~kivy.properties.StringProperty`
|
||
|
and defaults to `'out_sine'`.
|
||
|
"""
|
||
|
|
||
|
closing_time = NumericProperty(0.2)
|
||
|
"""
|
||
|
The time taken for the panel to slide to the :attr:`state` `'close'`.
|
||
|
|
||
|
:attr:`closing_time` is a :class:`~kivy.properties.NumericProperty`
|
||
|
and defaults to `0.2`.
|
||
|
"""
|
||
|
|
||
|
panel_cls = ObjectProperty()
|
||
|
"""
|
||
|
Panel object. The object must be one of the classes
|
||
|
:class:`~MDExpansionPanelOneLine`, :class:`~MDExpansionPanelTwoLine` or
|
||
|
:class:`~MDExpansionPanelThreeLine`.
|
||
|
|
||
|
:attr:`panel_cls` is a :class:`~kivy.properties.ObjectProperty`
|
||
|
and defaults to `None`.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
super().__init__(**kwargs)
|
||
|
self.register_event_type("on_open")
|
||
|
self.register_event_type("on_close")
|
||
|
|
||
|
if self.panel_cls and isinstance(
|
||
|
self.panel_cls,
|
||
|
(
|
||
|
MDExpansionPanelOneLine,
|
||
|
MDExpansionPanelTwoLine,
|
||
|
MDExpansionPanelThreeLine,
|
||
|
),
|
||
|
):
|
||
|
self.panel_cls.pos_hint = {"top": 1}
|
||
|
self.panel_cls._no_ripple_effect = True
|
||
|
self.panel_cls.bind(
|
||
|
on_release=lambda x: self.check_open_panel(self.panel_cls)
|
||
|
)
|
||
|
self.chevron = MDExpansionChevronRight()
|
||
|
self.panel_cls.add_widget(self.chevron)
|
||
|
if self.icon:
|
||
|
if self.icon in md_icons.keys():
|
||
|
self.panel_cls.add_widget(
|
||
|
IconLeftWidget(
|
||
|
icon=self.icon, pos_hint={"center_y": 0.5}
|
||
|
)
|
||
|
)
|
||
|
else:
|
||
|
self.panel_cls.add_widget(
|
||
|
ImageLeftWidget(
|
||
|
source=self.icon, pos_hint={"center_y": 0.5}
|
||
|
)
|
||
|
)
|
||
|
else:
|
||
|
# if no icon
|
||
|
self.panel_cls._txt_left_pad = m_res.HORIZ_MARGINS
|
||
|
self.add_widget(self.panel_cls)
|
||
|
else:
|
||
|
raise ValueError(
|
||
|
"KivyMD: `panel_cls` object must be must be one of the "
|
||
|
"objects from the list\n"
|
||
|
"[MDExpansionPanelOneLine, MDExpansionPanelTwoLine, "
|
||
|
"MDExpansionPanelThreeLine]"
|
||
|
)
|
||
|
|
||
|
def on_open(self, *args):
|
||
|
"""Called when a panel is opened."""
|
||
|
|
||
|
def on_close(self, *args):
|
||
|
"""Called when a panel is closed."""
|
||
|
|
||
|
def check_open_panel(self, instance):
|
||
|
"""
|
||
|
Called when you click on the panel. Called methods to open or close
|
||
|
a panel.
|
||
|
"""
|
||
|
|
||
|
press_current_panel = False
|
||
|
for panel in self.parent.children:
|
||
|
if isinstance(panel, MDExpansionPanel):
|
||
|
if len(panel.children) == 2:
|
||
|
if instance is panel.children[1]:
|
||
|
press_current_panel = True
|
||
|
panel.remove_widget(panel.children[0])
|
||
|
chevron = panel.children[0].children[0].children[0]
|
||
|
self.set_chevron_up(chevron)
|
||
|
self.close_panel(panel)
|
||
|
self.dispatch("on_close")
|
||
|
break
|
||
|
if not press_current_panel:
|
||
|
self.set_chevron_down()
|
||
|
|
||
|
def set_chevron_down(self):
|
||
|
"""Sets the chevron down."""
|
||
|
|
||
|
Animation(_angle=-90, d=self.opening_time).start(self.chevron)
|
||
|
self.open_panel()
|
||
|
self.dispatch("on_open")
|
||
|
|
||
|
def set_chevron_up(self, instance_chevron):
|
||
|
"""Sets the chevron up."""
|
||
|
|
||
|
Animation(_angle=0, d=self.closing_time).start(instance_chevron)
|
||
|
|
||
|
def close_panel(self, instance_panel):
|
||
|
"""Method closes the panel."""
|
||
|
|
||
|
Animation(
|
||
|
height=self.panel_cls.height,
|
||
|
d=self.closing_time,
|
||
|
t=self.closing_transition,
|
||
|
).start(instance_panel)
|
||
|
|
||
|
def open_panel(self, *args):
|
||
|
"""Method opens a panel."""
|
||
|
|
||
|
anim = Animation(
|
||
|
height=self.content.height + self.height,
|
||
|
d=self.opening_time,
|
||
|
t=self.opening_transition,
|
||
|
)
|
||
|
anim.bind(on_complete=self._add_content)
|
||
|
anim.start(self)
|
||
|
|
||
|
def add_widget(self, widget, index=0, canvas=None):
|
||
|
if isinstance(
|
||
|
widget,
|
||
|
(
|
||
|
MDExpansionPanelOneLine,
|
||
|
MDExpansionPanelTwoLine,
|
||
|
MDExpansionPanelThreeLine,
|
||
|
),
|
||
|
):
|
||
|
self.height = widget.height
|
||
|
return super().add_widget(widget)
|
||
|
|
||
|
def _add_content(self, *args):
|
||
|
if self.content:
|
||
|
try:
|
||
|
self.add_widget(self.content)
|
||
|
except WidgetException:
|
||
|
pass
|