530 lines
14 KiB
Python
530 lines
14 KiB
Python
"""
|
|
Components/ExpansionPanel
|
|
=========================
|
|
|
|
.. 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
|
|
|
|
MDExpansionPanel:
|
|
|
|
MDExpansionPanelHeader:
|
|
|
|
# Content of header.
|
|
[...]
|
|
|
|
MDExpansionPanelContent:
|
|
|
|
# Content of panel.
|
|
[...]
|
|
|
|
Anatomy
|
|
-------
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/expansion-panel-anatomy.png
|
|
:align: center
|
|
|
|
Example
|
|
-------
|
|
|
|
.. code-block:: python
|
|
|
|
from kivy.lang import Builder
|
|
from kivy.uix.behaviors import ButtonBehavior
|
|
|
|
from kivymd.app import MDApp
|
|
from kivymd.uix.behaviors import RotateBehavior
|
|
from kivymd.uix.expansionpanel import MDExpansionPanel
|
|
from kivymd.uix.list import MDListItemTrailingIcon
|
|
|
|
KV = '''
|
|
MDScreen:
|
|
md_bg_color: self.theme_cls.backgroundColor
|
|
|
|
MDExpansionPanel:
|
|
id: panel
|
|
pos_hint: {"center_x": .5, "center_y": .5}
|
|
|
|
MDExpansionPanelHeader:
|
|
|
|
MDListItem:
|
|
theme_bg_color: "Custom"
|
|
md_bg_color: self.theme_cls.surfaceContainerLowColor
|
|
ripple_effect: False
|
|
|
|
MDListItemSupportingText:
|
|
text: "Supporting text"
|
|
|
|
TrailingPressedIconButton:
|
|
id: chevron
|
|
icon: "chevron-right"
|
|
on_release: app.tap_expansion_chevron(panel, chevron)
|
|
|
|
MDExpansionPanelContent:
|
|
orientation: "vertical"
|
|
padding: "12dp", 0, "12dp", 0
|
|
|
|
MDLabel:
|
|
text: "Channel information"
|
|
adaptive_height: True
|
|
padding_x: "16dp"
|
|
padding_y: "12dp"
|
|
|
|
MDListItem:
|
|
|
|
MDListItemLeadingIcon:
|
|
icon: "email"
|
|
|
|
MDListItemHeadlineText:
|
|
text: "Email"
|
|
|
|
MDListItemSupportingText:
|
|
text: "kivydevelopment@gmail.com"
|
|
|
|
MDListItem:
|
|
|
|
MDListItemLeadingIcon:
|
|
icon: "instagram"
|
|
|
|
MDListItemHeadlineText:
|
|
text: "Instagram"
|
|
|
|
MDListItemSupportingText:
|
|
text: "Account"
|
|
|
|
MDListItemTertiaryText:
|
|
text: "www.instagram.com/KivyMD"
|
|
'''
|
|
|
|
|
|
class TrailingPressedIconButton(
|
|
ButtonBehavior, RotateBehavior, MDListItemTrailingIcon
|
|
):
|
|
...
|
|
|
|
|
|
class Example(MDApp):
|
|
def build(self):
|
|
self.theme_cls.theme_style = "Dark"
|
|
return Builder.load_string(KV)
|
|
|
|
def tap_expansion_chevron(
|
|
self, panel: MDExpansionPanel, chevron: TrailingPressedIconButton
|
|
):
|
|
panel.open() if not panel.is_open else panel.close()
|
|
panel.set_chevron_down(
|
|
chevron
|
|
) if not panel.is_open else panel.set_chevron_up(chevron)
|
|
|
|
|
|
Example().run()
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/expansion-panel-example.gif
|
|
:align: center
|
|
|
|
Use with ScrollView
|
|
-------------------
|
|
|
|
.. code-block:: python
|
|
|
|
import asynckivy
|
|
from kivy.animation import Animation
|
|
from kivy.lang import Builder
|
|
from kivy.metrics import dp
|
|
from kivy.uix.behaviors import ButtonBehavior
|
|
|
|
from kivymd.app import MDApp
|
|
from kivymd.uix.behaviors import RotateBehavior
|
|
from kivymd.uix.expansionpanel import MDExpansionPanel
|
|
from kivymd.uix.list import MDListItemTrailingIcon
|
|
|
|
KV = '''
|
|
<ExpansionPanelItem>
|
|
|
|
MDExpansionPanelHeader:
|
|
|
|
MDListItem:
|
|
theme_bg_color: "Custom"
|
|
md_bg_color: self.theme_cls.surfaceContainerLowColor
|
|
ripple_effect: False
|
|
|
|
MDListItemSupportingText:
|
|
text: "Supporting text"
|
|
|
|
TrailingPressedIconButton:
|
|
id: chevron
|
|
icon: "chevron-right"
|
|
on_release: app.tap_expansion_chevron(root, chevron)
|
|
|
|
MDExpansionPanelContent:
|
|
orientation: "vertical"
|
|
padding: "12dp", 0, "12dp", "12dp"
|
|
md_bg_color: self.theme_cls.surfaceContainerLowestColor
|
|
|
|
MDLabel:
|
|
text: "Channel information"
|
|
adaptive_height: True
|
|
padding_x: "16dp"
|
|
padding_y: "12dp"
|
|
|
|
MDListItem:
|
|
theme_bg_color: "Custom"
|
|
md_bg_color: self.theme_cls.surfaceContainerLowestColor
|
|
|
|
MDListItemLeadingIcon:
|
|
icon: "email"
|
|
|
|
MDListItemHeadlineText:
|
|
text: "Email"
|
|
|
|
MDListItemSupportingText:
|
|
text: "kivydevelopment@gmail.com"
|
|
|
|
MDListItem:
|
|
theme_bg_color: "Custom"
|
|
md_bg_color: self.theme_cls.surfaceContainerLowestColor
|
|
|
|
MDListItemLeadingIcon:
|
|
icon: "instagram"
|
|
|
|
MDListItemHeadlineText:
|
|
text: "Instagram"
|
|
|
|
MDListItemSupportingText:
|
|
text: "Account"
|
|
|
|
MDListItemTertiaryText:
|
|
text: "www.instagram.com/KivyMD"
|
|
|
|
|
|
MDScreen:
|
|
md_bg_color: self.theme_cls.backgroundColor
|
|
|
|
ScrollView:
|
|
size_hint_x: .5
|
|
pos_hint: {"center_x": .5, "center_y": .5}
|
|
|
|
MDList:
|
|
id: container
|
|
'''
|
|
|
|
|
|
class ExpansionPanelItem(MDExpansionPanel):
|
|
...
|
|
|
|
|
|
class TrailingPressedIconButton(
|
|
ButtonBehavior, RotateBehavior, MDListItemTrailingIcon
|
|
):
|
|
...
|
|
|
|
|
|
class Example(MDApp):
|
|
def on_start(self):
|
|
async def set_panel_list():
|
|
for i in range(12):
|
|
await asynckivy.sleep(0)
|
|
self.root.ids.container.add_widget(ExpansionPanelItem())
|
|
|
|
asynckivy.start(set_panel_list())
|
|
|
|
def build(self):
|
|
self.theme_cls.theme_style = "Dark"
|
|
return Builder.load_string(KV)
|
|
|
|
def tap_expansion_chevron(
|
|
self, panel: MDExpansionPanel, chevron: TrailingPressedIconButton
|
|
):
|
|
Animation(
|
|
padding=[0, dp(12), 0, dp(12)]
|
|
if not panel.is_open
|
|
else [0, 0, 0, 0],
|
|
d=0.2,
|
|
).start(panel)
|
|
panel.open() if not panel.is_open else panel.close()
|
|
panel.set_chevron_down(
|
|
chevron
|
|
) if not panel.is_open else panel.set_chevron_up(chevron)
|
|
|
|
|
|
Example().run()
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/expansion-panel-example-with-scroll-view.gif
|
|
:align: center
|
|
|
|
API break
|
|
=========
|
|
|
|
1.2.0 version
|
|
-------------
|
|
|
|
.. code-block:: python
|
|
|
|
MDExpansionPanel(
|
|
icon="icon.png",
|
|
content=Content(), # content of panel
|
|
panel_cls=MDExpansionPanelThreeLine( # content of header
|
|
text="Text",
|
|
secondary_text="Secondary text",
|
|
tertiary_text="Tertiary text",
|
|
)
|
|
)
|
|
|
|
2.0.0 version
|
|
-------------
|
|
|
|
.. code-block:: python
|
|
|
|
MDExpansionPanel:
|
|
|
|
MDExpansionPanelHeader:
|
|
|
|
# Content of header.
|
|
[...]
|
|
|
|
MDExpansionPanelContent:
|
|
|
|
# Content of panel.
|
|
[...]
|
|
"""
|
|
|
|
__all__ = (
|
|
"MDExpansionPanel",
|
|
"MDExpansionPanelContent",
|
|
"MDExpansionPanelHeader",
|
|
)
|
|
|
|
import os
|
|
|
|
from kivy.animation import Animation
|
|
from kivy.clock import Clock
|
|
from kivy.lang import Builder
|
|
from kivy.metrics import dp
|
|
from kivy.properties import (
|
|
NumericProperty,
|
|
ObjectProperty,
|
|
StringProperty,
|
|
BooleanProperty,
|
|
)
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
|
|
from kivymd import uix_path
|
|
from kivymd.theming import ThemableBehavior
|
|
from kivymd.uix.behaviors import BackgroundColorBehavior, DeclarativeBehavior
|
|
|
|
with open(
|
|
os.path.join(uix_path, "expansionpanel", "expansionpanel.kv"),
|
|
encoding="utf-8",
|
|
) as kv_file:
|
|
Builder.load_string(kv_file.read())
|
|
|
|
|
|
class MDExpansionPanelContent(
|
|
DeclarativeBehavior, ThemableBehavior, BackgroundColorBehavior, BoxLayout
|
|
):
|
|
"""
|
|
Implements a container for panel content.
|
|
|
|
.. versionadded:: 2.0.0
|
|
|
|
For more information, see in the
|
|
:class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
|
|
:class:`~kivymd.theming.ThemableBehavior` and
|
|
:class:`~kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior` and
|
|
:class:`~kivy.uix.boxlayout.BoxLayout`
|
|
classes documentation.
|
|
"""
|
|
|
|
|
|
class MDExpansionPanelHeader(DeclarativeBehavior, BoxLayout):
|
|
"""
|
|
Implements a container for the content of the panel header.
|
|
|
|
.. versionadded:: 2.0.0
|
|
|
|
For more information, see in the
|
|
:class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
|
|
:class:`~kivy.uix.boxlayout.BoxLayout`
|
|
classes documentation.
|
|
"""
|
|
|
|
|
|
# TODO: Add a successor from kivymd.uix.behaviors.motion_behavior.MotionBase
|
|
# to the MDExpansionPanel class to control the properties of the panel
|
|
# opening/closing animation.
|
|
class MDExpansionPanel(DeclarativeBehavior, BoxLayout):
|
|
"""
|
|
Expansion panel class.
|
|
|
|
For more information, see in the
|
|
:class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
|
|
:class:`~kivy.uix.boxlayout.BoxLayout`
|
|
classes documentation.
|
|
|
|
:Events:
|
|
:attr:`on_open`
|
|
Fired when a panel is opened.
|
|
:attr:`on_close`
|
|
Fired when a panel is closed.
|
|
"""
|
|
|
|
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`.
|
|
"""
|
|
|
|
is_open = BooleanProperty(False)
|
|
"""
|
|
The panel is open or closed.
|
|
|
|
.. versionadded:: 2.0.0
|
|
|
|
:attr:`is_open` is a :class:`~kivy.properties.BooleanProperty`
|
|
and defaults to `False`.
|
|
"""
|
|
|
|
_header = ObjectProperty() # MDExpansionPanelHeader object
|
|
_content = ObjectProperty() # MDExpansionPanelContent object
|
|
# Height of the MDExpansionPanelContent widget.
|
|
_original_content_height = NumericProperty()
|
|
_allow_add_content = False
|
|
_panel_is_process_opening = False
|
|
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.register_event_type("on_open")
|
|
self.register_event_type("on_close")
|
|
|
|
def on_open(self, *args) -> None:
|
|
"""Fired when a panel is opened."""
|
|
|
|
def on_close(self, *args) -> None:
|
|
"""Fired when a panel is closed."""
|
|
|
|
def set_chevron_down(self, instance) -> None:
|
|
"""Sets the chevron down."""
|
|
|
|
Animation(rotate_value_angle=-90, d=self.opening_time).start(instance)
|
|
|
|
def set_chevron_up(self, instance) -> None:
|
|
"""Sets the chevron up."""
|
|
|
|
Animation(rotate_value_angle=0, d=self.closing_time).start(instance)
|
|
|
|
def close(self, *args) -> None:
|
|
"""
|
|
Method closes the panel.
|
|
|
|
.. versionchanged:: 2.0.0
|
|
|
|
Rename from `close_panel` to `close` method.
|
|
"""
|
|
|
|
def set_content_height(*args):
|
|
anim_height = Animation(
|
|
height=0,
|
|
t=self.opening_transition,
|
|
d=self.opening_time,
|
|
)
|
|
anim_height.bind(
|
|
on_complete=lambda *args: self.remove_widget(self._content)
|
|
)
|
|
anim_height.start(self._content)
|
|
self.is_open = False
|
|
self.dispatch("on_close")
|
|
|
|
anim_opacity = Animation(
|
|
opacity=0,
|
|
t=self.opening_transition,
|
|
d=self.opening_time,
|
|
)
|
|
anim_opacity.bind(on_complete=set_content_height)
|
|
anim_opacity.start(self._content)
|
|
|
|
def open(self, *args) -> None:
|
|
"""
|
|
Method opens a panel.
|
|
|
|
.. versionchanged:: 2.0.0
|
|
|
|
Rename from `open_panel` to `open` method.
|
|
"""
|
|
|
|
def set_content_opacity(*args):
|
|
Animation(
|
|
opacity=1,
|
|
t=self.opening_transition,
|
|
d=self.opening_time,
|
|
).start(self._content)
|
|
self.is_open = True
|
|
self._panel_is_process_opening = False
|
|
self.dispatch("on_open")
|
|
|
|
if not self._panel_is_process_opening:
|
|
self._allow_add_content = True
|
|
self._panel_is_process_opening = True
|
|
self.add_widget(self._content)
|
|
|
|
anim_height = Animation(
|
|
height=self._original_content_height,
|
|
t=self.opening_transition,
|
|
d=self.opening_time,
|
|
)
|
|
anim_height.bind(on_complete=set_content_opacity)
|
|
anim_height.start(self._content)
|
|
|
|
def add_widget(self, widget, index=0, canvas=None):
|
|
if isinstance(widget, MDExpansionPanelHeader):
|
|
self._header = widget
|
|
return super().add_widget(widget)
|
|
elif (
|
|
isinstance(widget, MDExpansionPanelContent)
|
|
and not self._allow_add_content
|
|
):
|
|
self._content = widget
|
|
Clock.schedule_once(self._set_content_height, 0.8)
|
|
elif (
|
|
isinstance(widget, MDExpansionPanelContent)
|
|
and self._allow_add_content
|
|
):
|
|
return super().add_widget(widget)
|
|
|
|
def _set_content_height(self, *args):
|
|
self._original_content_height = self._content.height - dp(88)
|
|
self._content.height = 0
|