Reverted kivymd to avoid shader crash on Android

This commit is contained in:
Mark Qvist 2022-10-03 00:46:03 +02:00
parent 1595afaca3
commit bcd5c37101
97 changed files with 3446 additions and 5827 deletions

View File

@ -49,9 +49,6 @@ images_path = os.path.join(path, f"images{os.sep}")
uix_path = os.path.join(path, "uix")
"""Path to uix directory."""
glsl_path = os.path.join(path, "data", "glsl")
"""Path to glsl directory."""
_log_message = (
"KivyMD:"
+ (" Release" if release else "")

View File

@ -45,7 +45,7 @@ import os
from kivy.app import App
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.properties import ObjectProperty, StringProperty
from kivy.properties import ObjectProperty
from kivymd.theming import ThemeManager
@ -71,16 +71,6 @@ class MDApp(App, FpsMonitoring):
information.
"""
icon = StringProperty("kivymd/images/logo/kivymd-icon-512.png")
"""
See :attr:`~kivy.app.App.icon` attribute for more information.
.. versionadded:: 1.1.0
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
adn default to `kivymd/images/logo/kivymd-icon-512.png`.
"""
theme_cls = ObjectProperty()
"""
Instance of :class:`~ThemeManager` class.

View File

@ -412,7 +412,7 @@ To demonstrate the shades of the palette, you can run the following code:
self.screen = Factory.Root()
for name_tab in colors.keys():
tab = Tab(title=name_tab)
tab = Tab(text=name_tab)
self.screen.ids.android_tabs.add_widget(tab)
return self.screen
@ -427,7 +427,7 @@ To demonstrate the shades of the palette, you can run the following code:
{
"viewclass": "ItemColor",
"md_bg_color": colors[tab_text][value_color],
"title": value_color,
"text": value_color,
}
)

View File

@ -1,44 +0,0 @@
/*
The shader code has been refactored for the KivyMD library.
You can find the original code of this shaders at the links:
https://www.shadertoy.com/view/WtdSDs
https://www.shadertoy.com/view/fsdyzB
Additional thanks to iq for optimizing conditional block for individual
corner radius:
https://iquilezles.org/articles/distfunctions
*/
float roundedBoxSDF(vec2 centerPosition, vec2 size, vec4 radius) {
radius.xy = (centerPosition.x > 0.0) ? radius.xy : radius.zw;
radius.x = (centerPosition.y > 0.0) ? radius.x : radius.y;
vec2 q = abs(centerPosition) - (size - shadow_softness) + radius.x;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius.x;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Smooth the result (free antialiasing).
float edge0 = 0.0;
float smoothedAlpha = 1.0 - smoothstep(0.0, edge0, 1.0);
// Get the resultant shape.
vec4 quadColor = mix(
vec4(
shadow_color[0],
shadow_color[1],
shadow_color[2],
0.0
),
shadow_color,
smoothedAlpha
);
// Apply a drop shadow effect.
float shadowDistance = roundedBoxSDF(
fragCoord.xy - mouse.xy - (size / 2.0), size / 2.0, shadow_radius
);
float shadowAlpha = 1.0 - smoothstep(
-shadow_softness, shadow_softness, shadowDistance
);
fragColor = mix(quadColor, shadow_color, shadowAlpha - smoothedAlpha);
}

View File

@ -1,10 +0,0 @@
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 resolution;
uniform vec4 mouse;
uniform vec2 size;
uniform vec4 shadow_radius;
uniform float shadow_softness;
uniform vec4 shadow_color;

View File

@ -1,10 +0,0 @@
vec2 gfc(in vec4 fc) {
vec2 canvas_pos = resolution.zw;
vec2 uv = fc.xy;
uv.y -= canvas_pos.y;
return uv;
}
void main(void) {
mainImage(gl_FragColor, gfc(gl_FragCoord));
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1 @@
{"quad_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "quad_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "quad_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1 @@
{"rec_shadow-1.png": {"20": [2, 266, 256, 128], "21": [260, 266, 256, 128], "22": [518, 266, 256, 128], "23": [776, 266, 256, 128], "3": [260, 136, 256, 128], "2": [2, 136, 256, 128], "5": [776, 136, 256, 128], "4": [518, 136, 256, 128], "7": [260, 6, 256, 128], "6": [2, 6, 256, 128], "9": [776, 6, 256, 128], "8": [518, 6, 256, 128]}, "rec_shadow-0.png": {"11": [518, 266, 256, 128], "10": [260, 266, 256, 128], "13": [2, 136, 256, 128], "12": [776, 266, 256, 128], "15": [518, 136, 256, 128], "14": [260, 136, 256, 128], "17": [2, 6, 256, 128], "16": [776, 136, 256, 128], "19": [518, 6, 256, 128], "18": [260, 6, 256, 128], "1": [776, 6, 256, 128], "0": [2, 266, 256, 128]}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1 @@
{"rec_st_shadow-0.png": {"11": [262, 138, 128, 256], "10": [132, 138, 128, 256], "13": [522, 138, 128, 256], "12": [392, 138, 128, 256], "15": [782, 138, 128, 256], "14": [652, 138, 128, 256], "16": [912, 138, 128, 256], "0": [2, 138, 128, 256]}, "rec_st_shadow-1.png": {"20": [522, 138, 128, 256], "21": [652, 138, 128, 256], "17": [2, 138, 128, 256], "23": [912, 138, 128, 256], "19": [262, 138, 128, 256], "18": [132, 138, 128, 256], "22": [782, 138, 128, 256], "1": [392, 138, 128, 256]}, "rec_st_shadow-2.png": {"3": [132, 138, 128, 256], "2": [2, 138, 128, 256], "5": [392, 138, 128, 256], "4": [262, 138, 128, 256], "7": [652, 138, 128, 256], "6": [522, 138, 128, 256], "9": [912, 138, 128, 256], "8": [782, 138, 128, 256]}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1 @@
{"round_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "round_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "round_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}

View File

@ -35,15 +35,8 @@ assert "Icons" in LabelBase._fonts.keys() # NOQA
images = os.listdir(kivymd.images_path)
print(images)
assert "logo" in images
assert "alpha_layer.png" in images
assert "black.png" in images
assert "blue.png" in images
assert "red.png" in images
assert "green.png" in images
assert "yellow.png" in images
assert "folder.png" in images
assert "transparent.png" in images
assert "rec_shadow.atlas" in images
"""
)
pyi_main.run(

View File

@ -1,13 +1,14 @@
def test_create_project():
import os
import sys
os.system(
f"python3.10 -m kivymd.tools.patterns.create_project "
f"{sys.executable} -m kivymd.tools.patterns.create_project "
f"MVC "
f"{os.path.expanduser('~')} "
f"TestProject "
f"python3.10 "
f"stable "
f"{sys.executable} "
f"master "
f"--name_screen TestProjectScreen "
f"--name_database restdb "
f"--use_hotreload yes"

View File

@ -212,8 +212,9 @@ respects, the theming stays as documented.
dictionary :attr:`kivymd.color_definition.colors`.
"""
from kivy.animation import Animation
from kivy.app import App
from kivy.atlas import Atlas
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.event import EventDispatcher
@ -223,13 +224,13 @@ from kivy.properties import (
BooleanProperty,
ColorProperty,
DictProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
)
from kivy.utils import get_color_from_hex
from kivymd import images_path
from kivymd.color_definitions import colors, hue, palette
from kivymd.font_definitions import theme_font_styles
from kivymd.material_resources import DEVICE_IOS, DEVICE_TYPE
@ -623,152 +624,6 @@ class ThemeManager(EventDispatcher):
and defaults to `'M2'`.
"""
theme_style_switch_animation = BooleanProperty(False)
"""
Animate app colors when switching app color scheme ('Dark/light').
.. versionadded:: 1.1.0
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDCard:
orientation: "vertical"
padding: 0, 0, 0 , "36dp"
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
elevation: 4
shadow_radius: 6
shadow_offset: 0, 2
MDLabel:
text: "Theme style - {}".format(app.theme_cls.theme_style)
halign: "center"
valign: "center"
bold: True
font_style: "H5"
MDRaisedButton:
text: "Set theme"
on_release: app.switch_theme_style()
pos_hint: {"center_x": .5}
'''
class Example(MDApp):
def build(self):
self.theme_cls.theme_style_switch_animation = True
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def switch_theme_style(self):
self.theme_cls.primary_palette = (
"Orange" if self.theme_cls.primary_palette == "Red" else "Red"
)
self.theme_cls.theme_style = (
"Dark" if self.theme_cls.theme_style == "Light" else "Light"
)
Example().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.card import MDCard
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class Example(MDApp):
def build(self):
self.theme_cls.theme_style_switch_animation = True
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDCard(
MDLabel(
id="label",
text="Theme style - {}".format(self.theme_cls.theme_style),
halign="center",
valign="center",
bold=True,
font_style="H5",
),
MDRaisedButton(
text="Set theme",
on_release=self.switch_theme_style,
pos_hint={"center_x": 0.5},
),
id="card",
orientation="vertical",
padding=(0, 0, 0, "36dp"),
size_hint=(0.5, 0.5),
pos_hint={"center_x": 0.5, "center_y": 0.5},
elevation=4,
shadow_radius=6,
shadow_offset=(0, 2),
)
)
)
def switch_theme_style(self, *args):
self.theme_cls.primary_palette = (
"Orange" if self.theme_cls.primary_palette == "Red" else "Red"
)
self.theme_cls.theme_style = (
"Dark" if self.theme_cls.theme_style == "Light" else "Light"
)
self.root.ids.card.ids.label.text = (
"Theme style - {}".format(self.theme_cls.theme_style)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/theme-style-switch-animation.gif
:align: center
:attr:`theme_style_switch_animation` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
theme_style_switch_animation_duration = NumericProperty(0.2)
"""
Duration of the animation of switching the color scheme of the application
("Dark/light").
.. versionadded:: 1.1.0
.. code-block:: python
class Example(MDApp):
def build(self):
self.theme_cls.theme_style_switch_animation = True
self.theme_cls.theme_style_switch_animation_duration = 0.8
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/theme-style-switch-animation-duration.gif
:align: center
:attr:`theme_style_switch_animation_duration` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
theme_style = OptionProperty("Light", options=["Light", "Dark"])
"""
App theme style.
@ -1329,22 +1184,14 @@ class ThemeManager(EventDispatcher):
):
self.set_clearcolor_by_theme_style(theme_style)
_set_clearcolor = False
set_clearcolor = BooleanProperty(True)
def set_clearcolor_by_theme_style(self, theme_style):
if self.theme_style_switch_animation and self._set_clearcolor:
Animation(
clearcolor=get_color_from_hex(
self.colors[theme_style]["Background"]
),
d=self.theme_style_switch_animation_duration,
t="linear",
).start(Window)
else:
Window.clearcolor = get_color_from_hex(
self.colors[theme_style]["Background"]
)
self._set_clearcolor = True
if not self.set_clearcolor:
return
Window.clearcolor = get_color_from_hex(
self.colors[theme_style]["Background"]
)
# Font name, size (sp), always caps, letter spacing (sp).
font_styles = DictProperty(
@ -1551,6 +1398,10 @@ class ThemeManager(EventDispatcher):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rec_shadow = Atlas(f"{images_path}rec_shadow.atlas")
self.rec_st_shadow = Atlas(f"{images_path}rec_st_shadow.atlas")
self.quad_shadow = Atlas(f"{images_path}quad_shadow.atlas")
self.round_shadow = Atlas(f"{images_path}round_shadow.atlas")
Clock.schedule_once(lambda x: self.on_theme_style(0, self.theme_style))
self._determine_device_orientation(None, Window.size)
Window.bind(size=self._determine_device_orientation)
@ -1636,16 +1487,6 @@ class ThemableBehavior(EventDispatcher):
"""
def __init__(self, **kwargs):
self.unbind_properties = [
"theme_style",
"material_style",
"device_orientation",
"primary_color",
"primary_palette",
"accent_palette",
"text_color",
]
if self.theme_cls is not None:
pass
else:
@ -1666,24 +1507,3 @@ class ThemableBehavior(EventDispatcher):
)
self.theme_cls = App.get_running_app().theme_cls
super().__init__(**kwargs)
# def dec_disabled(self, *args, **kwargs) -> None:
# callabacks = self.theme_cls.get_property_observers("theme_style")
# for callaback in callabacks:
# try:
# if hasattr(callaback, "proxy") and hasattr(
# callaback.proxy, "theme_cls"
# ):
# for property_name in self.unbind_properties:
# self.theme_cls.unbind(
# **{
# property_name: getattr(
# callaback.proxy, callaback.method_name
# )
# }
# )
# except ReferenceError:
# pass
# super().dec_disabled(*args, **kwargs)

View File

@ -13,18 +13,6 @@ from pathlib import Path
import kivymd
datas = [
# Add `.frag` files from the `kivymd/data/glsl/elevation` directory.
(
str(Path(kivymd.glsl_path).joinpath("elevation")) + os.sep,
str(
Path("kivymd").joinpath(
str(Path(kivymd.glsl_path)).split(str(Path("kivymd")) + os.sep)[
1
]
+ f"{os.sep}elevation"
)
),
),
# Add `.ttf` files from the `kivymd/fonts` directory.
(
kivymd.fonts_path,

View File

@ -35,8 +35,8 @@
if not root.front_layer_color \
else root.front_layer_color
radius:
[root.radius_left, root.radius_right,
0, 0]
[root.radius_left, root.radius_left,
root.radius_right, root.radius_right]
OneLineListItem:
id: header_button

View File

@ -202,6 +202,7 @@ from kivy.uix.boxlayout import BoxLayout
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import MDCard
from kivymd.uix.floatlayout import MDFloatLayout
@ -527,5 +528,5 @@ class _BackLayer(BoxLayout):
pass
class _FrontLayer(MDCard):
class _FrontLayer(MDCard, FakeRectangularElevationBehavior):
pass

View File

@ -35,7 +35,7 @@ Usage
MDTopAppBar:
id: toolbar
title: "Example Banners"
elevation: 4
elevation: 10
pos_hint: {'top': 1}
MDBoxLayout:
@ -157,6 +157,7 @@ from kivy.properties import (
from kivy.uix.widget import Widget
from kivymd import uix_path
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.card import MDCard
@ -176,7 +177,7 @@ with open(
Builder.load_string(kv_file.read())
class MDBanner(MDCard):
class MDBanner(MDCard, FakeRectangularElevationBehavior):
vertical_pad = NumericProperty(dp(68))
"""
Indent the banner at the top of the screen.

View File

@ -11,20 +11,18 @@ from .backgroundcolor_behavior import (
)
# flake8: NOQA
from .declarative_behavior import DeclarativeBehavior
from .declarative_bahavior import DeclarativeBehavior
from .elevation import (
CircularElevationBehavior,
CommonElevationBehavior,
FakeCircularElevationBehavior,
FakeRectangularElevationBehavior,
ObservableShadow,
RectangularElevationBehavior,
RoundedRectangularElevationBehavior,
)
from .magic_behavior import MagicBehavior
from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
from .rotate_behavior import RotateBehavior
from .scale_behavior import ScaleBehavior
from .stencil_behavior import StencilBehavior
from .touch_behavior import TouchBehavior
from .hover_behavior import HoverBehavior # isort:skip

View File

@ -7,9 +7,8 @@ Behaviors/Background Color
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
from typing import List, Union
from typing import List
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import (
ColorProperty,
@ -25,6 +24,8 @@ from kivy.utils import get_color_from_hex
from kivymd.color_definitions import hue, palette, text_colors
from kivymd.theming import ThemeManager
from .elevation import CommonElevationBehavior
Builder.load_string(
"""
#:import RelativeLayout kivy.uix.relativelayout.RelativeLayout
@ -37,7 +38,7 @@ Builder.load_string(
angle: self.angle
origin: self._background_origin
Color:
rgba: self._md_bg_color
rgba: self.md_bg_color
RoundedRectangle:
group: "Background_instruction"
size: self.size
@ -66,7 +67,7 @@ Builder.load_string(
)
class BackgroundColorBehavior:
class BackgroundColorBehavior(CommonElevationBehavior):
background = StringProperty()
"""
Background image path.
@ -152,26 +153,15 @@ class BackgroundColorBehavior:
_background_x = NumericProperty(0)
_background_y = NumericProperty(0)
_background_origin = ReferenceListProperty(_background_x, _background_y)
_md_bg_color = ColorProperty([0, 0, 0, 0])
_background_origin = ReferenceListProperty(
_background_x,
_background_y,
)
def __init__(self, **kwarg):
super().__init__(**kwarg)
self.bind(pos=self.update_background_origin)
def on_md_bg_color(self, instance_md_widget, color: Union[list, str]):
if (
hasattr(self, "theme_cls")
and self.theme_cls.theme_style_switch_animation
):
Animation(
_md_bg_color=color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self._md_bg_color = color
def update_background_origin(
self, instance_md_widget, pos: List[float]
) -> None:
@ -216,14 +206,12 @@ class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
super().__init__(**kwargs)
if hasattr(self, "theme_cls"):
self.theme_cls.bind(
primary_palette=self._update_specific_text_color,
accent_palette=self._update_specific_text_color,
theme_style=self._update_specific_text_color,
primary_palette=self._update_specific_text_color
)
self.bind(
background_hue=self._update_specific_text_color,
background_palette=self._update_specific_text_color,
)
self.theme_cls.bind(accent_palette=self._update_specific_text_color)
self.theme_cls.bind(theme_style=self._update_specific_text_color)
self.bind(background_hue=self._update_specific_text_color)
self.bind(background_palette=self._update_specific_text_color)
self._update_specific_text_color(None, None)
def _update_specific_text_color(
@ -246,17 +234,5 @@ class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
secondary_color[3] = 0.54
else:
secondary_color[3] = 0.7
if (
hasattr(self, "theme_cls")
and self.theme_cls.theme_style_switch_animation
):
Animation(
specific_text_color=color,
specific_secondary_text_color=secondary_color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self.specific_text_color = color
self.specific_secondary_text_color = secondary_color
self.specific_text_color = color
self.specific_secondary_text_color = secondary_color

File diff suppressed because it is too large Load Diff

View File

@ -111,7 +111,7 @@ from kivy.properties import (
from kivy.uix.behaviors import ToggleButtonBehavior
class CommonRipple:
class CommonRipple(object):
"""Base class for ripple effect."""
ripple_rad_default = NumericProperty(1)

View File

@ -1,133 +0,0 @@
"""
Behaviors/Rotate
================
.. versionadded:: 1.1.0
Base class for controlling the rotate of the widget.
.. note:: See `kivy.graphics.Rotate
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Rotate>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.uix.button import Button
KV = '''
Screen:
RotateButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: 0, 0, 1
origin: self.center
canvas.after:
PopMatrix
'''
class RotateButton(Button):
rotate_value_angle = NumericProperty(0)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: Button) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
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.boxlayout import MDBoxLayout
KV = '''
MDScreen:
RotateBox:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
md_bg_color: "red"
'''
class RotateBox(ButtonBehavior, RotateBehavior, MDBoxLayout):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: RotateBox) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
"""
__all__ = ("RotateBehavior",)
from kivy.lang import Builder
from kivy.properties import ListProperty, NumericProperty
Builder.load_string(
"""
<RotateBehavior>
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: tuple(self.rotate_value_axis)
origin: self.center
canvas.after:
PopMatrix
"""
)
class RotateBehavior:
"""Base class for controlling the rotate of the widget."""
rotate_value_angle = NumericProperty(0)
"""
Property for getting/setting the angle of the rotation.
:attr:`rotate_value_angle` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
rotate_value_axis = ListProperty((0, 0, 1))
"""
Property for getting/setting the axis of the rotation.
:attr:`rotate_value_axis` is an :class:`~kivy.properties.ListProperty`
and defaults to `(0, 0, 1)`.
"""

View File

@ -1,156 +0,0 @@
"""
Behaviors/Scale
===============
.. versionadded:: 1.1.0
Base class for controlling the scale of the widget.
.. note:: See `kivy.graphics.Rotate
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Scale>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.button import Button
from kivy.app import App
KV = '''
Screen:
ScaleButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix
'''
class ScaleButton(Button):
scale_value_x = NumericProperty(1)
scale_value_y = NumericProperty(1)
scale_value_z = NumericProperty(1)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: Button) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivymd.app import MDApp
from kivymd.uix.behaviors import ScaleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
KV = '''
MDScreen:
ScaleBox:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
md_bg_color: "red"
'''
class ScaleBox(ButtonBehavior, ScaleBehavior, MDBoxLayout):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: ScaleBox) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
"""
__all__ = ("ScaleBehavior",)
from kivy.lang import Builder
from kivy.properties import NumericProperty
Builder.load_string(
"""
<ScaleBehavior>
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix
"""
)
class ScaleBehavior:
"""Base class for controlling the scale of the widget."""
scale_value_x = NumericProperty(1)
"""
X-axis value.
:attr:`scale_value_x` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
scale_value_y = NumericProperty(1)
"""
Y-axis value.
:attr:`scale_value_y` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
scale_value_z = NumericProperty(1)
"""
Z-axis value.
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""

View File

@ -1,134 +0,0 @@
"""
Behaviors/Stencil
=================
.. versionadded:: 1.1.0
Base class for controlling the stencil instructions of the widget.
.. note:: See `Stencil instructions
<https://kivy.org/doc/stable/api-kivy.graphics.stencil_instructions.html>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.lang import Builder
from kivy.app import App
KV = '''
Carousel:
Button:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
StencilPop
'''
class Test(App):
def build(self):
return Builder.load_string(KV)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import StencilBehavior
from kivymd.uix.fitimage import FitImage
KV = '''
#:import os os
#:import images_path kivymd.images_path
MDCarousel:
StencilImage:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
source: os.path.join(images_path, "logo", "kivymd-icon-512.png")
'''
class StencilImage(FitImage, StencilBehavior):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Test().run()
"""
__all__ = ("StencilBehavior",)
from kivy.lang import Builder
from kivy.properties import VariableListProperty
Builder.load_string(
"""
<StencilBehavior>
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilPop
"""
)
class StencilBehavior:
"""Base class for controlling the stencil instructions of the widget."""
radius = VariableListProperty([0], length=4)
"""
Canvas radius.
.. versionadded:: 1.0.0
.. code-block:: python
# Top left corner slice.
MDWidget:
radius: [25, 0, 0, 0]
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[0, 0, 0, 0]`.
"""

View File

@ -14,104 +14,61 @@ example:
pass
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.button import MDRectangleFlatButton
from kivy.lang import Builder
KV = '''
Screen:
from kivymd.app import MDApp
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.button import MDFlatButton
MDBoxLayout:
adaptive_size: True
pos_hint: {"center_x": .5, "center_y": .5}
KV = '''
MDScreen:
MyToggleButton:
text: "Show ads"
group: "x"
MDBoxLayout:
adaptive_size: True
spacing: "12dp"
pos_hint: {"center_x": .5, "center_y": .5}
MyToggleButton:
text: "Do not show ads"
group: "x"
MyToggleButton:
text: "Show ads"
group: "x"
MyToggleButton:
text: "Do not show ads"
group: "x"
MyToggleButton:
text: "Does not matter"
group: "x"
'''
MyToggleButton:
text: "Does not matter"
group: "x"
'''
class MyToggleButton(MDFlatButton, MDToggleButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_down = self.theme_cls.primary_color
class MyToggleButton(MDRectangleFlatButton, MDToggleButton):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.background_down = self.theme_cls.primary_light
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.screen import MDScreen
class MyToggleButton(MDFlatButton, MDToggleButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_down = self.theme_cls.primary_color
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBoxLayout(
MyToggleButton(
text="Show ads",
group="x",
),
MyToggleButton(
text="Do not show ads",
group="x",
),
MyToggleButton(
text="Does not matter",
group="x",
),
adaptive_size=True,
spacing="12dp",
pos_hint={"center_x": .5, "center_y": .5},
),
)
)
Test().run()
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-1.gif
:align: center
.. code-block:: python
class MyToggleButton(MDFillRoundFlatButton, MDToggleButton):
def __init__(self, **kwargs):
self.background_down = MDApp.get_running_app().theme_cls.primary_dark
super().__init__(**kwargs)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-2.gif
:align: center
You can inherit the ``MyToggleButton`` class only from the following classes
----------------------------------------------------------------------------
@ -131,7 +88,6 @@ from kivy.properties import BooleanProperty, ColorProperty
from kivy.uix.behaviors import ToggleButtonBehavior
from kivymd.uix.button import (
ButtonContentsIconText,
MDFillRoundFlatButton,
MDFillRoundFlatIconButton,
MDFlatButton,
@ -193,8 +149,7 @@ class MDToggleButton(ToggleButtonBehavior):
# Do the object inherited from the "supported" buttons?
if not issubclass(self.__class__, classinfo):
raise ValueError(
f"Class {self.__class__} must be inherited from one of the "
f"classes in the list {classinfo}"
f"Class {self.__class__} must be inherited from one of the classes in the list {classinfo}"
)
if (
not self.background_normal
@ -210,12 +165,10 @@ class MDToggleButton(ToggleButtonBehavior):
):
self.__is_filled = True
self.background_normal = self.theme_cls.primary_color
# If not background_normal must be the same as the inherited one.
# If not the background_normal must be the same as the inherited one:
else:
self.background_normal = (
self.md_bg_color[:] if self.md_bg_color else (0, 0, 0, 0)
)
# If no background_down is setter.
self.background_normal = self.md_bg_color[:]
# If no background_down is setted:
if (
not self.background_down
): # This means that if the value == [] or None will return True.
@ -247,6 +200,3 @@ class MDToggleButton(ToggleButtonBehavior):
): # If the background is transparent, the font color must be the
# primary color.
self.text_color = self.font_color_normal
if issubclass(self.__class__, ButtonContentsIconText):
self.icon_color = self.text_color

View File

@ -1,3 +1,4 @@
#:import sm kivy.uix.screenmanager
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
@ -6,9 +7,9 @@
height:
STANDARD_INCREMENT if app.theme_cls.material_style == "M2" else "80dp"
ScreenManager:
MDScreenManager:
id: tab_manager
transition: root.transition(duration=root.transition_duration)
transition: sm.FadeTransition(duration=.2)
on_current:
root.dispatch( \
"on_switch_tabs", \
@ -95,7 +96,7 @@
radius: [16,]
size: root._selected_region_width, dp(32)
pos:
self.center_x - root._selected_region_width / 2, \
self.center_x - self.width - dp(8), \
self.center_y - (dp(16))
MDLabel:

View File

@ -62,120 +62,59 @@ For ease of understanding, this code works like this:
Example
-------
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.app import MDApp
class Test(MDApp):
class Test(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
return Builder.load_string(
'''
MDScreen:
def build(self):
self.theme_cls.material_style = "M3"
return Builder.load_string(
'''
MDScreen:
MDBottomNavigation:
#panel_color: "#eeeaea"
selected_color_background: "orange"
text_color_active: "lightgrey"
MDBottomNavigation:
panel_color: "#eeeaea"
selected_color_background: "#97ecf8"
text_color_active: 0, 0, 0, 1
MDBottomNavigationItem:
name: 'screen 1'
text: 'Mail'
icon: 'gmail'
badge_icon: "numeric-10"
MDBottomNavigationItem:
name: 'screen 1'
text: 'Mail'
icon: 'gmail'
badge_icon: "numeric-10"
MDLabel:
text: 'Mail'
halign: 'center'
MDLabel:
text: 'Mail'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 2'
text: 'Twitter'
icon: 'twitter'
badge_icon: "numeric-5"
MDBottomNavigationItem:
name: 'screen 2'
text: 'Discord'
icon: 'discord'
badge_icon: "numeric-5"
MDLabel:
text: 'Twitter'
halign: 'center'
MDLabel:
text: 'Discord'
halign: 'center'
MDBottomNavigationItem:
name: 'screen 3'
text: 'LinkedIN'
icon: 'linkedin'
MDBottomNavigationItem:
name: 'screen 3'
text: 'LinkedIN'
icon: 'linkedin'
MDLabel:
text: 'LinkedIN'
halign: 'center'
'''
)
MDLabel:
text: 'LinkedIN'
halign: 'center'
'''
)
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class Test(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
return (
MDScreen(
MDBottomNavigation(
MDBottomNavigationItem(
MDLabel(
text='Mail',
halign='center',
),
name='screen 1',
text='Mail',
icon='gmail',
badge_icon="numeric-10",
),
MDBottomNavigationItem(
MDLabel(
text='Twitter',
halign='center',
),
name='screen 1',
text='Twitter',
icon='twitter',
badge_icon="numeric-10",
),
MDBottomNavigationItem(
MDLabel(
text='LinkedIN',
halign='center',
),
name='screen 1',
text='LinkedIN',
icon='linkedin',
badge_icon="numeric-10",
),
selected_color_background="orange",
text_color_active="lightgrey",
)
)
)
Test().run()
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation.gif
:align: center
@ -253,13 +192,16 @@ from kivy.properties import (
)
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import FadeTransition, ScreenManagerException
from kivy.uix.screenmanager import ScreenManagerException
from kivymd import uix_path
from kivymd.material_resources import STANDARD_INCREMENT
from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.uix.anchorlayout import MDAnchorLayout
from kivymd.uix.behaviors import CommonElevationBehavior, DeclarativeBehavior
from kivymd.uix.behaviors import (
DeclarativeBehavior,
FakeRectangularElevationBehavior,
)
from kivymd.uix.behaviors.backgroundcolor_behavior import (
SpecificBackgroundColorBehavior,
)
@ -471,28 +413,6 @@ class MDBottomNavigationItem(MDTab):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def animate_header(
self, bottom_navigation_object, bottom_navigation_header_object
) -> None:
if bottom_navigation_object.use_text:
Animation(_label_font_size=sp(12), d=0.1).start(
bottom_navigation_object.previous_tab.header
)
Animation(
_selected_region_width=0,
t="in_out_sine",
d=0,
).start(bottom_navigation_header_object)
Animation(
_text_color_normal=bottom_navigation_header_object.text_color_normal
if bottom_navigation_object.previous_tab.header.text_color_normal
!= [1, 1, 1, 1]
else self.theme_cls.disabled_hint_text_color,
d=0.1,
).start(bottom_navigation_object.previous_tab.header)
bottom_navigation_object.previous_tab.header.active = False
self.header.active = True
def on_tab_press(self, *args) -> None:
"""Called when clicking on a panel item."""
@ -500,13 +420,28 @@ class MDBottomNavigationItem(MDTab):
bottom_navigation_header_object = (
bottom_navigation_object.previous_tab.header
)
bottom_navigation_object.ids.tab_manager.current = self.name
if bottom_navigation_object.previous_tab is not self:
self.animate_header(
bottom_navigation_object, bottom_navigation_header_object
)
super().on_tab_press(*args)
if bottom_navigation_object.use_text:
Animation(_label_font_size=sp(12), d=0.1).start(
bottom_navigation_object.previous_tab.header
)
Animation(
_selected_region_width=0,
t="in_out_sine",
d=0,
).start(bottom_navigation_header_object)
Animation(
_text_color_normal=bottom_navigation_header_object.text_color_normal
if bottom_navigation_object.previous_tab.header.text_color_normal
!= [1, 1, 1, 1]
else self.theme_cls.disabled_hint_text_color,
d=0.1,
).start(bottom_navigation_object.previous_tab.header)
bottom_navigation_object.previous_tab.header.active = False
self.header.active = True
bottom_navigation_object.previous_tab = self
def on_disabled(
self, instance_bottom_navigation_item, disabled_value: bool
@ -563,26 +498,6 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
.. versionadded:: 1.0.0
"""
transition = ObjectProperty(FadeTransition)
"""
Transition animation of bottom navigation screen manager.
.. versionadded:: 1.1.0
:attr:`transition` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `FadeTransition`.
"""
transition_duration = NumericProperty(0.2)
"""
Duration animation of bottom navigation screen manager.
.. versionadded:: 1.1.0
:attr:`transition_duration` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
text_color_normal = ColorProperty([1, 1, 1, 1])
"""
Text color of the label when it is not selected.
@ -857,6 +772,8 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
class MDBottomNavigationBar(
ThemableBehavior, CommonElevationBehavior, MDFloatLayout
ThemableBehavior,
FakeRectangularElevationBehavior,
MDFloatLayout,
):
pass

View File

@ -34,7 +34,7 @@ Usage :class:`~MDListBottomSheet`
MDTopAppBar:
title: "Example BottomSheet"
pos_hint: {"top": 1}
elevation: 4
elevation: 10
MDRaisedButton:
text: "Open list bottom sheet"
@ -94,7 +94,7 @@ which will be used as an icon to the left of the item:
MDTopAppBar:
title: 'Example BottomSheet'
pos_hint: {"top": 1}
elevation: 4
elevation: 10
MDRaisedButton:
text: "Open grid bottom sheet"
@ -180,7 +180,7 @@ which will be used as an icon to the left of the item:
MDTopAppBar:
title: 'Example BottomSheet'
pos_hint: {"top": 1}
elevation: 4
elevation: 10
MDRaisedButton:
text: "Open custom bottom sheet"

View File

@ -93,8 +93,6 @@ from kivymd.uix.behaviors import DeclarativeBehavior
class MDBoxLayout(DeclarativeBehavior, BoxLayout, MDAdaptiveWidget):
"""
Box layout class.
For more information, see in the
Box layout class. For more information, see in the
:class:`~kivy.uix.boxlayout.BoxLayout` class documentation.
"""

View File

@ -1,7 +1,6 @@
# NOQA F401
from .button import (
BaseButton,
ButtonContentsIconText,
MDFillRoundFlatButton,
MDFillRoundFlatIconButton,
MDFlatButton,

View File

@ -3,9 +3,9 @@
Clear
Color:
rgba:
self._md_bg_color \
(self._md_bg_color or [0.0, 0.0, 0.0, 0.0]) \
if not self.disabled else \
self._md_bg_color_disabled
(self._md_bg_color_disabled or [0.0, 0.0, 0.0, 0.0])
RoundedRectangle:
size: self.size
pos: self.pos
@ -13,17 +13,19 @@
radius: [root._radius, ]
Color:
rgba:
root._line_color \
root._line_color or [0.0, 0.0, 0.0, 0.0] \
if not root.disabled else \
(root._line_color_disabled or self._disabled_color)
( \
root._line_color_disabled \
or self._disabled_color \
or [0.0, 0.0, 0.0, 0.0] \
)
Line:
width: root.line_width
rounded_rectangle:
( \
self.x, self.y, self.width, self.height, \
(self.x, self.y, self.width, self.height, \
root._radius, root._radius, root._radius, root._radius, \
self.height \
)
self.height)
size_hint: None, None
anchor_x: root.halign
@ -31,28 +33,21 @@
_round_rad: [self._radius] * 4
<ButtonContentsText>
lbl_txt: lbl_txt
width:
max( \
root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
)
max(root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2])
size_hint_min_x:
max( \
root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
)
max(root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2])
height:
max( \
root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
)
max(root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3])
size_hint_min_y:
max( \
root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
)
max(root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3])
MDLabel:
id: lbl_txt
@ -89,10 +84,7 @@
# This is only a temporary fix and does not fix the cause of the error.
(root._icon_color if root._icon_color else root.theme_cls.text_color) \
if not root.disabled else \
root.theme_cls.disabled_hint_text_color \
if not root.disabled_color else \
root.disabled_color
root.theme_cls.disabled_hint_text_color
on_icon:
if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
theme_text_color: root._theme_icon_color
@ -139,7 +131,7 @@
id: box
adaptive_size: True
padding: 0
spacing: "8dp"
spacing: "4dp"
MDIcon:
id: lbl_ic
@ -201,21 +193,48 @@
radius: [self.height / 2]
<MDFloatingRootButton>
<BaseFloatingRootButton>
theme_text_color: "Custom"
md_bg_color: self.theme_cls.primary_color
<MDFloatingLabel>
padding_x: "8dp"
padding_y: "8dp"
adaptive_size: True
theme_text_color: "Custom"
canvas.before:
PushMatrix
Rotate:
angle: self._angle
axis: (0, 0, 1)
origin: self.center
canvas.after:
PopMatrix
# FIXME: Use :class:`~kivymd.uix.boxlayout.MDBoxLayout` instead
# :class:`~kivy.uix.boxlayout.BoxLayout`.
<BaseFloatingLabel>
size_hint: None, None
padding: "8dp", "4dp", "8dp", "4dp"
height: label.texture_size[1] + self.padding[1] * 2
width: label.texture_size[0] + self.padding[0] * 2
elevation: 10
# TODO: Use `md_bg_color` and `radius` instead `canvasю
canvas:
Color:
rgba: self.bg_color
rgba:
self.theme_cls.primary_color \
if not root.bg_color else \
root.bg_color
RoundedRectangle:
size: self.size
pos: self.pos
radius: self.radius
size: self.size
radius: [5]
Label:
id: label
markup: True
text: root.text
size_hint: None, None
size: self.texture_size
color:
root.theme_cls.text_color \
if not root.text_color else \
root.text_color

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,17 @@
md_bg_color: app.theme_cls.divider_color
<MDCard>
canvas.before:
Color:
rgba: self.md_bg_color
RoundedRectangle:
size: self.size
pos: self.pos
radius: root.radius
source: root.background
<MDSeparator>
md_bg_color:
self.theme_cls.divider_color \

View File

@ -26,6 +26,26 @@ Components/Card
MDCard
------
.. warning:: Starting from the KivyMD 1.1.0 library version, it is necessary
to manually inherit the card class from one of the ``Elevation`` classes
from ``kivymd/uix/behaviors/elevation.py`` module to draw the card shadow.
.. code-block:: python
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
It actually allows for better control over the providers that implement the
rendering of the shadows.
.. note:: You can read more information about the classes that implement the
rendering of shadows on this
`documentation page <https://kivymd.readthedocs.io/en/latest/behaviors/elevation/>`_.
An example of the implementation of a card in the style of material design version 3
------------------------------------------------------------------------------------
@ -39,6 +59,7 @@ An example of the implementation of a card in the style of material design versi
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
KV = '''
@ -72,7 +93,7 @@ An example of the implementation of a card in the style of material design versi
'''
class MD3Card(MDCard):
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
text = StringProperty()
@ -105,6 +126,7 @@ An example of the implementation of a card in the style of material design versi
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDIconButton
from kivymd.uix.card import MDCard
@ -113,7 +135,7 @@ An example of the implementation of a card in the style of material design versi
from kivymd.uix.screen import MDScreen
class MD3Card(MDCard):
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
@ -148,6 +170,7 @@ An example of the implementation of a card in the style of material design versi
adaptive_size=True,
color="grey",
pos=("12dp", "12dp"),
bold=True,
),
),
line_color=(0.2, 0.2, 0.2, 0.8),
@ -232,9 +255,10 @@ End full code
MDBoxLayout:
orientation: "vertical"
spacing: "10dp"
MDTopAppBar:
elevation: 4
elevation: 10
title: "MDCardSwipe"
MDScrollView:
@ -262,7 +286,7 @@ End full code
'''Creates a list of cards.'''
for i in range(20):
self.root.ids.md_list.add_widget(
self.screen.ids.md_list.add_widget(
SwipeToDeleteItem(text=f"One-line item {i}")
)
@ -292,7 +316,7 @@ End full code
MDScreen(
MDBoxLayout(
MDTopAppBar(
elevation=4,
elevation=10,
title="MDCardSwipe",
),
MDScrollView(
@ -304,6 +328,7 @@ End full code
),
id="box",
orientation="vertical",
spacing="10dp",
),
)
)
@ -401,7 +426,7 @@ You can use this event to remove items from a list:
.. code-block:: python
def on_swipe_complete(self, instance):
self.root.ids.md_list.remove_widget(instance)
self.screen.ids.md_list.remove_widget(instance)
.. tab:: Decralative python styles
@ -471,9 +496,10 @@ End full code
MDBoxLayout:
orientation: "vertical"
spacing: "10dp"
MDTopAppBar:
elevation: 4
elevation: 10
title: "MDCardSwipe"
MDScrollView:
@ -534,7 +560,7 @@ End full code
MDScreen(
MDBoxLayout(
MDTopAppBar(
elevation=4,
elevation=10,
title="MDCardSwipe",
),
MDScrollView(
@ -546,6 +572,7 @@ End full code
),
id="box",
orientation="vertical",
spacing="10dp",
),
)
)
@ -603,21 +630,26 @@ Focus behavior
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.card import MDCard
KV = '''
MDScreen:
MDCard:
ElevationCard:
size_hint: .7, .4
focus_behavior: True
pos_hint: {"center_x": .5, "center_y": .5}
md_bg_color: "darkgrey"
unfocus_color: "darkgrey"
focus_color: "grey"
elevation: 6
'''
class ElevationCard(MDCard, FakeRectangularElevationBehavior):
pass
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
@ -631,23 +663,27 @@ Focus behavior
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.card import MDCard
from kivymd.uix.screen import MDScreen
class ElevationCard(MDCard, FakeRectangularElevationBehavior):
pass
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return (
MDScreen(
MDCard(
ElevationCard(
size_hint=(0.7, 0.4),
focus_behavior=True,
pos_hint={"center_x": 0.5, "center_y": 0.5},
md_bg_color="darkgrey",
unfocus_color="darkgrey",
focus_color="grey",
elevation=6,
),
)
)
@ -655,6 +691,7 @@ Focus behavior
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/card-focus.gif
:align: center
@ -695,14 +732,12 @@ from kivy.properties import (
VariableListProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.utils import get_color_from_hex
from kivymd import uix_path
from kivymd.color_definitions import colors
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
BackgroundColorBehavior,
CommonElevationBehavior,
DeclarativeBehavior,
RectangularRippleBehavior,
)
@ -746,7 +781,6 @@ class MDCard(
ThemableBehavior,
BackgroundColorBehavior,
RectangularRippleBehavior,
CommonElevationBehavior,
FocusBehavior,
BoxLayout,
):
@ -766,6 +800,14 @@ class MDCard(
and defaults to `False`.
"""
elevation = NumericProperty(None, allownone=True)
"""
Elevation value.
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to 1.
"""
radius = VariableListProperty([dp(6), dp(6), dp(6), dp(6)])
"""
Card radius by default.
@ -789,16 +831,15 @@ class MDCard(
"""
_bg_color_map = (
get_color_from_hex(colors["Light"]["CardsDialogs"]),
get_color_from_hex(colors["Dark"]["CardsDialogs"]),
colors["Light"]["CardsDialogs"],
colors["Dark"]["CardsDialogs"],
[1.0, 1.0, 1.0, 0.0],
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.theme_cls.bind(
material_style=self.set_style, theme_style=self.update_md_bg_color
)
self.theme_cls.bind(theme_style=self.update_md_bg_color)
self.theme_cls.bind(material_style=self.set_style)
Clock.schedule_once(self.set_style)
Clock.schedule_once(
lambda x: self.on_ripple_behavior(0, self.ripple_behavior)
@ -807,9 +848,7 @@ class MDCard(
def update_md_bg_color(self, instance_card, theme_style: str) -> None:
if self.md_bg_color in self._bg_color_map:
self.md_bg_color = get_color_from_hex(
colors[theme_style]["CardsDialogs"]
)
self.md_bg_color = colors[theme_style]["CardsDialogs"]
def set_style(self, *args) -> None:
self.set_radius()
@ -826,7 +865,7 @@ class MDCard(
if self.style == "outlined" or self.style == "filled":
self.elevation = 0
elif self.style == "elevated":
self.elevation = 2
self.elevation = 1
def set_radius(self) -> None:
if (

View File

@ -132,7 +132,7 @@ Use with elevation
icon_right: "close-circle-outline"
line_color: app.theme_cls.disabled_hint_text_color
md_bg_color: 1, 0, 0, .5
elevation: 4
elevation: 12
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chip-with-elevation.png
:align: center
@ -304,6 +304,7 @@ __all__ = ("MDChip",)
import os
from kivy import Logger
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.metrics import dp
@ -313,13 +314,14 @@ from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
CommonElevationBehavior,
FakeRectangularElevationBehavior,
RectangularRippleBehavior,
ScaleBehavior,
TouchBehavior,
)
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDIcon
from kivymd.uix.stacklayout import MDStackLayout
from kivymd.uix.templates import ScaleWidget
with open(
os.path.join(uix_path, "chip", "chip.kv"), encoding="utf-8"
@ -328,12 +330,12 @@ with open(
class MDChip(
MDBoxLayout,
ThemableBehavior,
RectangularRippleBehavior,
ButtonBehavior,
CommonElevationBehavior,
FakeRectangularElevationBehavior,
TouchBehavior,
ButtonBehavior,
MDBoxLayout,
):
text = StringProperty()
"""
@ -454,7 +456,7 @@ class MDChip(
self.active = False
class MDScalableCheckIcon(MDIcon, ScaleBehavior):
class MDScalableCheckIcon(MDIcon, ScaleWidget):
pos_hint = {"center_y": 0.5}

View File

@ -1,4 +1,5 @@
#:import DEVICE_TYPE kivymd.material_resources.DEVICE_TYPE
#:import FakeRectangularElevationBehavior kivymd.uix.behaviors.FakeRectangularElevationBehavior
<CellRow>
@ -65,7 +66,7 @@
size_hint_y: None
height: self.minimum_height
spacing: "4dp"
tooltip_text: root.tooltip if root.tooltip else root.text
tooltip_text: root.text
BoxLayout:
id: box
@ -174,11 +175,7 @@
font_size: "14sp"
on_release: root.table_data.open_pagination_menu()
text:
"{}".format( \
root.table_data.rows_num \
if root.table_data.rows_num < len(root.table_data.row_data) else \
len(root.table_data.row_data) \
)
f"{root.table_data.rows_num if root.table_data.rows_num < len(root.table_data.row_data) else len(root.table_data.row_data)}"
Widget:
size_hint_x: None
@ -195,11 +192,9 @@
if root.theme_cls.theme_style == "Dark" else \
(0, 0, 0, 1)
text:
"1-{} of {}".format( \
root.table_data.rows_num \
if root.table_data.rows_num > len(root.table_data.row_data) else \
len(root.table_data.row_data), len(root.table_data.row_data) \
)
f"1-" \
f"{root.table_data.rows_num if root.table_data.rows_num > len(root.table_data.row_data) else len(root.table_data.row_data)} " \
f"of {len(root.table_data.row_data)}"
MDIconButton:
id: button_back
@ -222,7 +217,7 @@
on_release: root.table_data.set_next_row_data_parts("forward")
<TableContainer@MDCard>
<TableContainer@MDCard+FakeRectangularElevationBehavior>
<MDDataTable>

View File

@ -11,6 +11,19 @@ Components/DataTables
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-previous.png
:align: center
Warnings
---------
.. warning:: Data tables are still far from perfect. The class is in constant
change, because of optimizations and bug fixes. If you find a bug or have
an improvement you want to share, take some time and share your discoveries
with us over the main git repo.
Any help is well appreciated.
.. warning:: In versions prior to `Kivy 2.1.0-dev0` exists an error in which is
the table has only one row in the current page, the table will only render
one column instead of the whole row.
.. note:: `MDDataTable` allows developers to sort the data provided by column.
This happens thanks to the use of an external function that you can bind
while you're defining the table columns. Be aware that the sorting function
@ -146,15 +159,6 @@ class CellHeader(MDTooltip, BoxLayout):
and defaults to `''`.
"""
tooltip = StringProperty()
"""
Tooltip containing descriptive text for the column.
If the tooltip is not provided, column `text` shall be used instead.
:attr:`tooltip` is a :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
# TODO: Added example.
sort_action = ObjectProperty()
"""
@ -336,19 +340,11 @@ class TableHeader(ThemableBehavior, ScrollView):
CellHeader(
text=col_heading[0],
sort_action=col_heading[2],
tooltip=col_heading[3],
width=self.cols_minimum[i],
table_data=self.table_data,
is_sorted=(col_heading[0] == self.sorted_on),
sorted_order=self.sorted_order,
)
if len(col_heading) == 4
else CellHeader(
text=col_heading[0],
sort_action=col_heading[2],
width=self.cols_minimum[i],
table_data=self.table_data,
)
if len(col_heading) == 3
else CellHeader(
text=col_heading[0],
@ -360,9 +356,6 @@ class TableHeader(ThemableBehavior, ScrollView):
else:
# Sets the text in the first cell.
self.ids.first_cell.text = col_heading[0]
self.ids.first_cell.tooltip = (
col_heading[3] if len(col_heading) == 4 else ""
)
self.ids.first_cell.ids.separator.height = 0
self.ids.first_cell.width = self.cols_minimum[i]
@ -772,9 +765,6 @@ class TablePagination(ThemableBehavior, MDBoxLayout):
class MDDataTable(ThemableBehavior, AnchorLayout):
"""
See :class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation for
more information.
:Events:
:attr:`on_row_press`
Called when a table row is clicked.
@ -785,6 +775,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
.. code-block:: python
from kivy.metrics import dp
from kivymd.app import MDApp
@ -923,82 +914,38 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Data for header columns.
.. tabs::
.. code-block:: python
.. tab:: Imperative python style
from kivy.metrics import dp
.. code-block:: python
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivy.uix.anchorlayout import AnchorLayout
from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivy.uix.anchorlayout import AnchorLayout
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
self.data_tables = MDDataTable(
size_hint=(0.7, 0.6),
use_pagination=True,
check=True,
# name column, width column, sorting function column(optional), custom tooltip
column_data=[
("No.", dp(30), None, "Custom tooltip"),
("Status", dp(30)),
("Signal Name", dp(60)),
("Severity", dp(30)),
("Stage", dp(30)),
("Schedule", dp(30), lambda *args: print("Sorted using Schedule")),
("Team Lead", dp(30)),
],
)
layout.add_widget(self.data_tables)
return layout
class Example(MDApp):
def build(self):
layout = AnchorLayout()
self.data_tables = MDDataTable(
size_hint=(0.7, 0.6),
use_pagination=True,
check=True,
# name column, width column, sorting function column(optional)
column_data=[
("No.", dp(30)),
("Status", dp(30)),
("Signal Name", dp(60)),
("Severity", dp(30)),
("Stage", dp(30)),
("Schedule", dp(30), lambda *args: print("Sorted using Schedule")),
("Team Lead", dp(30)),
],
)
layout.add_widget(self.data_tables)
return layout
Example().run()
.. tab:: Declarative python style
.. code-block:: python
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.anchorlayout import MDAnchorLayout
from kivymd.uix.datatables import MDDataTable
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return MDAnchorLayout(
MDDataTable(
size_hint=(0.7, 0.6),
use_pagination=True,
check=True,
# name column, width column, sorting function column(optional)
column_data=[
("No.", dp(30)),
("Status", dp(30)),
("Signal Name", dp(60)),
("Severity", dp(30)),
("Stage", dp(30)),
("Schedule", dp(30),
lambda *args: print("Sorted using Schedule")),
("Team Lead", dp(30)),
],
)
)
Example().run()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-column-data.png
:align: center
@ -1113,9 +1060,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1243,7 +1187,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Use or not use checkboxes for rows.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-check.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-check.gif
:align: center
:attr:`check` is an :class:`~kivy.properties.BooleanProperty`
@ -1265,9 +1209,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1297,19 +1238,19 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
and defaults to `False`.
"""
elevation = NumericProperty(4)
elevation = NumericProperty(8)
"""
Table elevation.
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `4`.
and defaults to `8`.
"""
rows_num = NumericProperty(5)
"""
The number of rows displayed on one page of the table.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-use-pagination-rows-num.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-use-pagination.gif
:align: center
:attr:`rows_num` is an :class:`~kivy.properties.NumericProperty`
@ -1325,7 +1266,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
.. rubric:: Center
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-pos-top.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-pos-center.png
:align: center
.. rubric:: Auto
@ -1341,6 +1282,11 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Menu height for selecting the number of displayed rows.
.. rubric:: 140dp
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-height-140.png
:align: center
.. rubric:: 240dp
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-height-240.png
@ -1352,7 +1298,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color = ColorProperty([0, 0, 0, 0])
"""
Background color in the format (r, g, b, a) or string format.
Background color in the format (r, g, b, a).
See :attr:`~kivy.uix.modalview.ModalView.background_color`.
Use markup strings
@ -1369,9 +1315,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1411,8 +1354,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_header = ColorProperty(None)
"""
Background color in the format (r, g, b, a) or string format for
:class:`~TableHeader` class.
Background color for :class:`~TableHeader` class.
.. versionadded:: 1.0.0
@ -1432,8 +1374,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_cell = ColorProperty(None)
"""
Background color in the format (r, g, b, a) or string format for
:class:`~CellRow` class.
Background color for :class:`~CellRow` class.
.. versionadded:: 1.0.0
@ -1454,8 +1395,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_selected_cell = ColorProperty(None)
"""
Background selected color in the format (r, g, b, a) or string format for
:class:`~CellRow` class.
Background selected color for :class:`~CellRow` class.
.. versionadded:: 1.0.0
@ -1468,7 +1408,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_selected_cell="e4514f",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-background-color-selected-cell.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-background-color-selected-cell.gif
:align: center
:attr:`background_color_selected_cell` is a :class:`~kivy.properties.ColorProperty` and
@ -1563,9 +1503,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
data_tables = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = MDFloatLayout() # root layout
# Creating control buttons.
button_box = MDBoxLayout(
@ -1667,9 +1604,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
data_tables = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = MDFloatLayout()
layout.add_widget(
MDRaisedButton(

View File

@ -18,11 +18,7 @@
PopMatrix
<DialogContainer@MDCard>
shadow_color: 0.0, 0.0, 0.0, 0.0
elevation: 0
shadow_softness: 0
shadow_offset: 0, 0
<DialogContainer@MDCard+FakeRectangularElevationBehavior>
<MDDialog>
@ -32,6 +28,7 @@
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
elevation: 24
padding: "24dp", "24dp", "8dp", "8dp"
radius: root.radius
md_bg_color:

View File

@ -38,8 +38,6 @@ Usage
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_alert_dialog(self):
@ -89,7 +87,6 @@ from kivy.uix.modalview import ModalView
from kivymd import uix_path
from kivymd.material_resources import DEVICE_TYPE
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import CommonElevationBehavior
from kivymd.uix.button import BaseButton
from kivymd.uix.card import MDSeparator
from kivymd.uix.list import BaseListItem
@ -100,40 +97,7 @@ with open(
Builder.load_string(kv_file.read())
class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
elevation = NumericProperty(3)
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `3`.
"""
shadow_softness = NumericProperty(24)
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_softness`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`shadow_softness` is an :class:`~kivy.properties.NumericProperty`
and defaults to `24`.
"""
shadow_offset = ListProperty((0, 4))
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_offset`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
and defaults to `[0, 4]`.
"""
class BaseDialog(ThemableBehavior, ModalView):
radius = ListProperty([dp(7), dp(7), dp(7), dp(7)])
"""
Dialog corners rounding value.
@ -286,22 +250,21 @@ class MDDialog(BaseDialog):
class Example(MDApp):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def build(self):
return Builder.load_string(KV)
def show_simple_dialog(self):
if not self.dialog:
self.dialog = MDDialog(
title="Set backup account",
type="simple",
items=[
Item(text="user01@gmail.com", source="kivymd/images/logo/kivymd-icon-128.png"),
Item(text="user02@gmail.com", source="data/logo/kivy-icon-128.png"),
],
)
self.dialog.open()
def show_simple_dialog(self):
if not self.dialog:
self.dialog = MDDialog(
title="Set backup account",
type="simple",
items=[
Item(text="user01@gmail.com", source="user-1.png"),
Item(text="user02@gmail.com", source="user-2.png"),
Item(text="Add account", source="add-icon.png"),
],
)
self.dialog.open()
Example().run()
@ -354,8 +317,6 @@ class MDDialog(BaseDialog):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_confirmation_dialog(self):
@ -424,140 +385,71 @@ class MDDialog(BaseDialog):
"""
Custom content class.
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
KV = '''
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "120dp"
from kivymd.app import MDApp
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
MDTextField:
hint_text: "City"
KV = '''
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "120dp"
MDTextField:
hint_text: "City"
MDTextField:
hint_text: "Street"
MDTextField:
hint_text: "Street"
MDFloatLayout:
MDFloatLayout:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_confirmation_dialog()
'''
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_confirmation_dialog()
'''
class Content(BoxLayout):
pass
class Content(BoxLayout):
pass
class Example(MDApp):
dialog = None
class Example(MDApp):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def build(self):
return Builder.load_string(KV)
def show_confirmation_dialog(self):
if not self.dialog:
self.dialog = MDDialog(
title="Address:",
type="custom",
content_cls=Content(),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
],
)
self.dialog.open()
def show_confirmation_dialog(self):
if not self.dialog:
self.dialog = MDDialog(
title="Address:",
type="custom",
content_cls=Content(),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
],
)
self.dialog.open()
Example().run()
.. tab:: Declarative Python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.textfield import MDTextField
class Example(MDApp):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDFloatLayout(
MDFlatButton(
text="ALERT DIALOG",
pos_hint={'center_x': 0.5, 'center_y': 0.5},
on_release=self.show_confirmation_dialog,
)
)
)
def show_confirmation_dialog(self, *args):
if not self.dialog:
self.dialog = MDDialog(
title="Address:",
type="custom",
content_cls=MDBoxLayout(
MDTextField(
hint_text="City",
),
MDTextField(
hint_text="Street",
),
orientation="vertical",
spacing="12dp",
size_hint_y=None,
height="120dp",
),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
],
)
self.dialog.open()
Example().run()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/dialog-custom.png
:align: center
@ -568,7 +460,7 @@ class MDDialog(BaseDialog):
md_bg_color = ColorProperty(None)
"""
Background color in the (r, g, b, a) or string format.
Background color in the format (r, g, b, a).
:attr:`md_bg_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.

View File

@ -1,7 +1,7 @@
<_Triangle>:
canvas:
Color:
rgba: app.theme_cls.text_color
rgba: root.theme_cls.text_color
Triangle:
points:
[ \
@ -13,8 +13,7 @@
<MDDropDownItem>
orientation: "vertical"
size_hint: None, None
size: self.minimum_size
adaptive_size: True
spacing: "5dp"
padding: "5dp", "5dp", "5dp", 0

View File

@ -15,13 +15,13 @@ Usage
from kivymd.app import MDApp
KV = '''
MDScreen
Screen
MDDropDownItem:
id: drop_item
pos_hint: {'center_x': .5, 'center_y': .5}
text: 'Item'
on_release: print("Press item")
on_release: self.set_item("New Item")
'''
@ -48,12 +48,12 @@ import os
from kivy.lang import Builder
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import DeclarativeBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
with open(
os.path.join(uix_path, "dropdownitem", "dropdownitem.kv"), encoding="utf-8"
@ -61,12 +61,15 @@ with open(
Builder.load_string(kv_file.read())
class _Triangle(Widget):
class _Triangle(ThemableBehavior, Widget):
pass
class MDDropDownItem(
DeclarativeBehavior, ThemableBehavior, ButtonBehavior, BoxLayout
ThemableBehavior,
FakeRectangularElevationBehavior,
ButtonBehavior,
MDBoxLayout,
):
text = StringProperty()
"""

View File

@ -6,29 +6,28 @@
background_normal: ""
background_down: ""
dir_or_file_name: ""
icon_color: 0, 0, 0, 0
_selected: False
events_callback: lambda x: None
orientation: "vertical"
ModifiedOneLineIconListItem:
text: root.dir_or_file_name
on_release: root.events_callback(root.path, root)
bg_color:
self.theme_cls.bg_darkest \
if root._selected else \
self.theme_cls.bg_normal
if root._selected else self.theme_cls.bg_normal
on_release: root.events_callback(root.path, root)
IconLeftWidget:
icon: root.icon
theme_icon_color: "Custom"
icon_color: root.icon_color
theme_text_color: "Custom"
text_color: self.theme_cls.primary_color
MDSeparator:
<LabelContent@MDLabel>
adaptive_height: True
size_hint_y: None
height: self.texture_size[1]
shorten: True
shorten_from: "center"
halign: "center"
@ -62,6 +61,23 @@
text: root.name
<FloatButton>
anchor_x: "right"
anchor_y: "bottom"
size_hint_y: None
height: dp(56)
padding: dp(10)
MDFloatingActionButton:
size_hint: None, None
size:dp(56), dp(56)
icon: root.icon
opposite_colors: True
elevation: 8
on_release: root.callback()
md_bg_color: root.md_bg_color
<MDFileManager>
md_bg_color: root.theme_cls.bg_normal
@ -74,11 +90,7 @@
title: root.current_path
right_action_items: [["close-box", lambda x: root.exit_manager(1)]]
left_action_items: [["chevron-left", lambda x: root.back()]]
elevation: 3
md_bg_color:
app.theme_cls.primary_color \
if not root.background_color_toolbar else \
root.background_color_toolbar
elevation: 10
RecycleView:
id: rv

View File

@ -9,7 +9,7 @@ Usage
.. code-block:: python
path = os.path.expanduser("~") # path to the directory that will be opened in the file manager
path = '/' # path to the directory that will be opened in the file manager
file_manager = MDFileManager(
exit_manager=self.exit_manager, # function called when the user reaches directory tree root
select_path=self.select_path, # function called when selecting a file/directory
@ -19,7 +19,7 @@ Usage
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager.png
:align: center
.. warning:: Be careful! To use the `'/'` path on Android devices, you need
.. warning:: Be careful! To use the `/` path on Android devices, you need
special permissions. Therefore, you are likely to get an error.
Or with ``preview`` mode:
@ -32,7 +32,7 @@ Or with ``preview`` mode:
preview=True,
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-preview.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-previous.png
:align: center
.. warning:: The `preview` mode is intended only for viewing images and will
@ -43,8 +43,6 @@ Example
.. code-block:: python
import os
from kivy.core.window import Window
from kivy.lang import Builder
@ -55,19 +53,19 @@ Example
KV = '''
MDBoxLayout:
orientation: "vertical"
orientation: 'vertical'
MDTopAppBar:
title: "MDFileManager"
left_action_items: [["menu", lambda x: None]]
elevation: 3
left_action_items: [['menu', lambda x: None]]
elevation: 10
MDFloatLayout:
MDRoundFlatIconButton:
text: "Open manager"
icon: "folder"
pos_hint: {"center_x": .5, "center_y": .5}
pos_hint: {'center_x': .5, 'center_y': .6}
on_release: app.file_manager_open()
'''
@ -78,23 +76,23 @@ Example
Window.bind(on_keyboard=self.events)
self.manager_open = False
self.file_manager = MDFileManager(
exit_manager=self.exit_manager, select_path=self.select_path
exit_manager=self.exit_manager,
select_path=self.select_path,
preview=True,
)
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def file_manager_open(self):
self.file_manager.show(os.path.expanduser("~")) # output manager to the screen
self.file_manager.show('/') # output manager to the screen
self.manager_open = True
def select_path(self, path: str):
'''
It will be called when you click on the file name
def select_path(self, path):
'''It will be called when you click on the file name
or the catalog selection button.
:type path: str;
:param path: path to the selected directory or file;
'''
@ -128,9 +126,6 @@ Not tested on `iOS`.
def file_manager_open(self):
self.file_manager.show_disks()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-show-disks.png
:align: center
"""
__all__ = ("MDFileManager",)
@ -141,7 +136,6 @@ import re
from typing import List, Tuple, Union
from kivy import platform
from kivy.clock import Clock
from kivy.factory import Factory
from kivy.lang import Builder
from kivy.metrics import dp
@ -154,6 +148,7 @@ from kivy.properties import (
OptionProperty,
StringProperty,
)
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.modalview import ModalView
@ -161,7 +156,6 @@ from kivymd import images_path, uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import CircularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFloatingActionButton
from kivymd.uix.fitimage import FitImage
from kivymd.uix.list import BaseListItem
from kivymd.uix.relativelayout import MDRelativeLayout
@ -173,7 +167,9 @@ with open(
class BodyManager(MDBoxLayout):
"""Base class for folders and files icons."""
"""
Base class for folders and files icons.
"""
class BodyManagerWithPreview(MDBoxLayout):
@ -186,146 +182,47 @@ class IconButton(CircularRippleBehavior, ButtonBehavior, FitImage):
"""Folder icons/thumbnails images in ``preview`` mode."""
class FloatButton(ThemableBehavior, AnchorLayout):
callback = ObjectProperty()
md_bg_color = ColorProperty([1, 1, 1, 1])
icon = StringProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.bind(primary_palette=self.set_md_bg_color)
def set_md_bg_color(self, *args):
self.md_bg_color = self.theme_cls.primary_color
class ModifiedOneLineIconListItem(BaseListItem):
_txt_left_pad = NumericProperty("72dp")
_txt_top_pad = NumericProperty("16dp")
_txt_bot_pad = NumericProperty("15dp")
_num_lines = 1
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.height = dp(48)
class MDFileManager(MDRelativeLayout, ThemableBehavior):
class MDFileManager(ThemableBehavior, MDRelativeLayout):
icon = StringProperty("check")
"""
Implements a modal dialog with a file manager.
For more information, see in the
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
:Events:
`on_pre_open`:
Called before the MDFileManager is opened.
`on_open`:
Called when the MDFileManager is opened.
`on_pre_dismiss`:
Called before the MDFileManager is closed.
`on_dismiss`:
Called when the MDFileManager is closed.
"""
icon = StringProperty("check", deprecated=True)
"""
Icon that will be used on the directory selection button.
.. deprecated:: 1.1.0
Use :attr:`icon_selection_button` instead.
The icon that will be used on the directory selection button.
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `check`.
"""
icon_selection_button = StringProperty("check")
"""
Icon that will be used on the directory selection button.
.. versionadded:: 1.1.0
.. code-block:: python
MDFileManager(
...
icon_selection_button="pencil",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-icon-selection-button.png
:align: center
:attr:`icon_selection_button` is an :class:`~kivy.properties.StringProperty`
and defaults to `check`.
"""
background_color_selection_button = ColorProperty(None)
"""
Background color of the current directory/path selection button.
.. versionadded:: 1.1.0
.. code-block:: python
MDFileManager(
...
background_color_selection_button="brown",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-background-color-selection-button.png
:align: center
:attr:`background_color_selection_button` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
background_color_toolbar = ColorProperty(None)
"""
Background color of the file manager toolbar.
.. versionadded:: 1.1.0
.. code-block:: python
MDFileManager(
...
background_color_toolbar="brown",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-background-color-toolbar.png
:align: center
:attr:`background_color_toolbar` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_folder = StringProperty(f"{images_path}folder.png")
"""
Icon that will be used for folder icons when using ``preview = True``.
.. code-block:: python
MDFileManager(
...
preview=True,
icon_folder="path/to/icon.png",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-icon-folder.png
:align: center
The icon that will be used for folder icons when using ``preview = True``.
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `check`.
"""
icon_color = ColorProperty(None)
"""
Color of the folder icon when the :attr:`preview` property is set to False.
.. versionadded:: 1.1.0
.. code-block:: python
MDFileManager(
...
preview=False,
icon_color="brown",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/file-manager-icon-color.png
:align: center
:attr:`icon_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
exit_manager = ObjectProperty(lambda x: None)
"""
Function called when the user reaches directory tree root.
@ -362,12 +259,12 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
and defaults to `all`.
"""
current_path = StringProperty(os.path.expanduser("~"))
current_path = StringProperty(os.getcwd())
"""
Current directory.
:attr:`current_path` is an :class:`~kivy.properties.StringProperty`
and defaults to `os.path.expanduser("~")`.
and defaults to `/`.
"""
use_access = BooleanProperty(True)
@ -398,9 +295,9 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
"name", options=["nothing", "name", "date", "size", "type"]
)
"""
It can take the values 'nothing' 'name' 'date' 'size' 'type' - sorts files
by option. By default, sort by name. Available options are:
`'nothing'`, `'name'`, `'date'`, `'size'`, `'type'`.
It can take the values 'nothing' 'name' 'date' 'size' 'type' - sorts files by option
By default, sort by name.
Available options are: `'nothing'`, `'name'`, `'date'`, `'size'`, `'type'`.
:attr:`sort_by` is an :class:`~kivy.properties.OptionProperty`
and defaults to `name`.
@ -428,33 +325,29 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
"""
Contains the list of files that are currently selected.
:attr:`selection` is a read-only :class:`~kivy.properties.ListProperty`
and defaults to `[]`.
"""
selection_button = ObjectProperty()
"""
The instance of the directory/path selection button.
.. versionadded:: 1.1.0
:attr:`selection_button` is a read-only :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
:attr:`selection` is a read-only :class:`~kivy.properties.ListProperty` and
defaults to `[]`.
"""
_window_manager = None
_window_manager_open = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_event_type("on_pre_open")
self.register_event_type("on_open")
self.register_event_type("on_pre_dismiss")
self.register_event_type("on_dismiss")
def __init__(self, **kwargs):
super().__init__(**kwargs)
toolbar_label = self.ids.toolbar.children[1].children[0]
toolbar_label.font_style = "Subtitle1"
Clock.schedule_once(self._create_selection_button)
if (
self.selector == "any"
or self.selector == "multi"
or self.selector == "folder"
):
self.add_widget(
FloatButton(
callback=self.select_directory_on_press_button,
md_bg_color=self.theme_cls.primary_color,
icon=self.icon,
)
)
if self.preview:
self.ext = [".png", ".jpg", ".jpeg"]
@ -507,7 +400,15 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
}
)
self.ids.rv.data = manager_list
self._show()
if not self._window_manager:
self._window_manager = ModalView(
size_hint=self.size_hint, auto_dismiss=False
)
self._window_manager.add_widget(self)
if not self._window_manager_open:
self._window_manager.open()
self._window_manager_open = True
def show(self, path: str) -> None:
"""
@ -573,9 +474,6 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
"icon": icon,
"dir_or_file_name": name,
"events_callback": self.select_dir_or_file,
"icon_color": self.theme_cls.primary_color
if not self.icon_color
else self.icon_color,
"_selected": False,
}
)
@ -590,14 +488,19 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
"icon": "file-outline",
"dir_or_file_name": os.path.split(name)[1],
"events_callback": self.select_dir_or_file,
"icon_color": self.theme_cls.primary_color
if not self.icon_color
else self.icon_color,
"_selected": False,
}
)
self.ids.rv.data = manager_list
self._show()
if not self._window_manager:
self._window_manager = ModalView(
size_hint=self.size_hint, auto_dismiss=False
)
self._window_manager.add_widget(self)
if not self._window_manager_open:
self._window_manager.open()
self._window_manager_open = True
def get_access_string(self, path: str) -> str:
access_string = ""
@ -654,9 +557,7 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
def close(self) -> None:
"""Closes the file manager window."""
self.dispatch("on_pre_dismiss")
self._window_manager.dismiss()
self.dispatch("on_dismiss")
self._window_manager_open = False
def select_dir_or_file(
@ -708,84 +609,6 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
if self.selector == "folder" or self.selector == "any":
self.select_path(self.current_path)
def on_icon(self, instance_file_manager, icon_name: str) -> None:
"""Called when the :attr:`icon` property is changed."""
self.icon_selection_button = icon_name
def on_background_color_toolbar(
self, instance_file_manager, color: Union[str, list]
) -> None:
"""
Called when the :attr:`background_color_toolbar` property is changed.
"""
def on_background_color_toolbar(*args):
self.ids.toolbar.md_bg_color = color
Clock.schedule_once(on_background_color_toolbar)
def on_pre_open(self, *args):
"""
Default pre-open event handler.
.. versionadded:: 1.1.0
"""
def on_open(self, *args):
"""
Default open event handler.
.. versionadded:: 1.1.0
"""
def on_pre_dismiss(self, *args):
"""
Default pre-dismiss event handler.
.. versionadded:: 1.1.0
"""
def on_dismiss(self, *args):
"""
Default dismiss event handler.
.. versionadded:: 1.1.0
"""
def _show(self):
if not self._window_manager:
self._window_manager = ModalView(
size_hint=self.size_hint, auto_dismiss=False
)
self.size_hint = (1, 1)
self._window_manager.add_widget(self)
if not self._window_manager_open:
self._window_manager.open()
self._window_manager_open = True
self.dispatch("on_pre_open")
self.dispatch("on_open")
def _create_selection_button(self, *args):
if (
self.selector == "any"
or self.selector == "multi"
or self.selector == "folder"
):
self.selection_button = MDFloatingActionButton(
on_release=self.select_directory_on_press_button,
md_bg_color=self.theme_cls.primary_color
if not self.background_color_selection_button
else self.background_color_selection_button,
icon=self.icon_selection_button,
pos_hint={"right": 0.99},
y=dp(12),
elevation=0,
)
self.add_widget(self.selection_button)
def __sort_files(self, files):
def sort_by_name(files):
files.sort(key=locale.strxfrm)

View File

@ -132,11 +132,11 @@ from kivy.properties import BooleanProperty, ObjectProperty
from kivy.uix.image import AsyncImage
from kivy.uix.widget import Widget
from kivymd.uix.behaviors import StencilBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.templates import StencilWidget
class FitImage(MDBoxLayout, StencilBehavior):
class FitImage(MDBoxLayout, StencilWidget):
source = ObjectProperty()
"""
Filename/source of your image.

View File

@ -90,7 +90,4 @@ from kivymd.uix.behaviors import DeclarativeBehavior
class MDGridLayout(DeclarativeBehavior, GridLayout, MDAdaptiveWidget):
"""
Grid layout class. For more information, see in the
:class:`~kivy.uix.gridlayout.GridLayout` class documentation.
"""
pass

View File

@ -63,7 +63,7 @@ Base example
x: 24
FitImage:
source: "kivymd/images/logo/kivymd-icon-512.png"
source: "https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png"
size_hint: None, None
size: hero_from.size
@ -72,7 +72,7 @@ Base example
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen B"
MDScreen:
@ -82,7 +82,6 @@ Base example
MDHeroTo:
id: hero_to
tag: "hero"
size_hint: None, None
size: "220dp", "220dp"
pos_hint: {"center_x": .5, "center_y": .5}
@ -92,7 +91,7 @@ Base example
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen A"
'''
@ -114,7 +113,6 @@ Note that the child of the :class:`~MDHeroFrom` widget must have the size of the
MDHeroFrom:
id: hero_from
tag: "hero"
FitImage:
size_hint: None, None
@ -129,7 +127,7 @@ container in which the hero is located:
MDRaisedButton:
text: "Move Hero To Screen B"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen 2"
If you need to switch to a screen that does not contain heroes, set the
@ -140,7 +138,7 @@ If you need to switch to a screen that does not contain heroes, set the
MDRaisedButton:
text: "Go To Another Screen"
on_release:
root.current_heroes = []
root.current_hero = ""
root.current = "another screen"
Example
@ -168,7 +166,7 @@ Example
x: 24
FitImage:
source: "kivymd/images/logo/kivymd-icon-512.png"
source: "https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png"
size_hint: None, None
size: hero_from.size
@ -177,7 +175,7 @@ Example
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen B"
MDScreen:
@ -187,7 +185,6 @@ Example
MDHeroTo:
id: hero_to
tag: "hero"
size_hint: None, None
size: "220dp", "220dp"
pos_hint: {"center_x": .5, "center_y": .5}
@ -197,7 +194,7 @@ Example
pos_hint: {"center_x": .5}
y: "52dp"
on_release:
root.current_heroes = []
root.current_hero = ""
root.current = "screen C"
MDRaisedButton:
@ -205,7 +202,7 @@ Example
pos_hint: {"center_x": .5}
y: "8dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen A"
MDScreen:
@ -286,7 +283,7 @@ background color of the hero during the flight between the screens:
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen B"
MDScreen:
@ -296,7 +293,6 @@ background color of the hero during the flight between the screens:
MDHeroTo:
id: hero_to
tag: "hero"
size_hint: None, None
size: "220dp", "220dp"
pos_hint: {"center_x": .5, "center_y": .5}
@ -306,7 +302,7 @@ background color of the hero during the flight between the screens:
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["hero"]
root.current_hero = "hero"
root.current = "screen A"
'''
@ -374,7 +370,7 @@ Usage with ScrollView
radius: 24
box_radius: 0, 0, 24, 24
box_color: 0, 0, 0, .5
source: "kivymd/images/logo/kivymd-icon-512.png"
source: "image.jpg"
size_hint: None, None
size: root.size
mipmap: True
@ -403,7 +399,7 @@ Usage with ScrollView
MDScreen:
name: "screen B"
heroes_to: [hero_to]
hero_to: hero_to
MDHeroTo:
id: hero_to
@ -416,7 +412,7 @@ Usage with ScrollView
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = [hero_to.tag]
root.current_hero = "hero"
root.current = "screen A"
'''
@ -445,8 +441,7 @@ Usage with ScrollView
def on_release(self):
def switch_screen(*args):
self.manager.current_heroes = [self.tag]
self.manager.ids.hero_to.tag = self.tag
self.manager.current_hero = self.tag
self.manager.current = "screen B"
Clock.schedule_once(switch_screen, 0.2)
@ -470,93 +465,6 @@ Usage with ScrollView
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-usage-with-scrollview.gif
:align: center
Using multiple heroes at the same time
--------------------------------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreenManager:
MDScreen:
name: "screen A"
md_bg_color: "lightblue"
MDHeroFrom:
id: hero_kivymd
tag: "kivymd"
size_hint: None, None
size: "200dp", "200dp"
pos_hint: {"top": .98}
x: 24
FitImage:
source: "kivymd/images/logo/kivymd-icon-512.png"
size_hint: None, None
size: hero_kivymd.size
MDHeroFrom:
id: hero_kivy
tag: "kivy"
size_hint: None, None
size: "200dp", "200dp"
pos_hint: {"top": .98}
x: 324
FitImage:
source: "data/logo/kivy-icon-512.png"
size_hint: None, None
size: hero_kivy.size
MDRaisedButton:
text: "Move Hero To Screen B"
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["kivymd", "kivy"]
root.current = "screen B"
MDScreen:
name: "screen B"
heroes_to: hero_to_kivymd, hero_to_kivy
md_bg_color: "cadetblue"
MDHeroTo:
id: hero_to_kivy
tag: "kivy"
size_hint: None, None
pos_hint: {"center_x": .5, "center_y": .5}
MDHeroTo:
id: hero_to_kivymd
tag: "kivymd"
size_hint: None, None
pos_hint: {"right": 1, "top": 1}
MDRaisedButton:
text: "Move Hero To Screen A"
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = ["kivy", "kivymd"]
root.current = "screen A"
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-multiple-heroes.gif
:align: center
"""
from kivy.properties import StringProperty
@ -568,9 +476,6 @@ class MDHeroFrom(MDBoxLayout):
"""
The container from which the hero begins his flight.
For more information, see in the
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
:Events:
`on_transform_in`
when the hero flies from screen **A** to screen **B**.
@ -582,7 +487,7 @@ class MDHeroFrom(MDBoxLayout):
"""
Tag ID for heroes.
:attr:`tag` is an :class:`~kivy.properties.StringProperty`
:attr:`shift_right` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
@ -599,17 +504,4 @@ class MDHeroFrom(MDBoxLayout):
class MDHeroTo(MDBoxLayout):
"""
The container in which the hero comes.
For more information, see in the
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
"""
tag = StringProperty(allownone=True)
"""
Tag ID for heroes.
:attr:`tag` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
"""The container in which the hero comes."""

View File

@ -65,13 +65,12 @@ Implementation
:align: center
"""
__all__ = [
"MDSmartTile",
]
__all__ = "MDSmartTile"
import os
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.properties import (
BooleanProperty,
ColorProperty,

View File

@ -17,14 +17,8 @@
rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0)
Rectangle:
source: self.source if self.source else None
pos:
self.pos \
if not self.source else \
(self.x - self._size[0] / 2, self.y)
size:
self._size \
if self.source else \
self.size
pos: self.pos
size: self.size
font_style: "Icon"
text: u"{}".format(md_icons[root.icon]) if root.icon in md_icons else "blank"

View File

@ -222,18 +222,14 @@ __all__ = ("MDLabel", "MDIcon")
import os
from typing import Union
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.lang import Builder
from kivy.metrics import sp
from kivy.properties import (
AliasProperty,
BooleanProperty,
ColorProperty,
ListProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
)
@ -326,7 +322,6 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
parent_background = ColorProperty(None)
can_capitalize = BooleanProperty(True)
canvas_bg = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
@ -354,7 +349,6 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
font_info = self.theme_cls.font_styles[self.font_style]
self.font_name = font_info[0]
self.font_size = sp(font_info[1])
if font_info[2] and self.can_capitalize:
self._capitalizing = True
else:
@ -380,68 +374,29 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
# generic None value it's not yet been set
self._text_color_str = ""
if theme_text_color == "Custom" and self.text_color:
color = self.text_color
self.color = self.text_color
elif (
theme_text_color == "ContrastParentBackground"
and self.parent_background
):
color = get_contrast_text_color(self.parent_background)
self.color = get_contrast_text_color(self.parent_background)
else:
color = [0, 0, 0, 1]
self.color = [0, 0, 0, 1]
if self.theme_cls.theme_style_switch_animation:
Animation(
color=color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self.color = color
def on_text_color(self, instance_label, color: Union[list, str]) -> None:
def on_text_color(self, instance_label, color: list) -> None:
if self.theme_text_color == "Custom":
if self.theme_cls.theme_style_switch_animation:
Animation(
color=self.text_color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self.color = self.text_color
self.color = self.text_color
def on_opposite_colors(self, *args) -> None:
self.on_theme_text_color(self, self.theme_text_color)
def on_md_bg_color(self, instance_label, color: Union[list, str]) -> None:
self.canvas.remove_group("Background_instruction")
with self.canvas.before:
Color(rgba=color)
self.canvas_bg = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_canvas_bg_pos)
def on_size(self, instance_label, size: list) -> None:
if self.canvas_bg:
self.canvas_bg.size = size
def update_canvas_bg_pos(self, instance_label, pos: list) -> None:
if self.canvas_bg:
self.canvas_bg.pos = pos
def _do_update_theme_color(self, *args):
if self._text_color_str:
self.color = getattr(self.theme_cls, self._text_color_str)
if not self.disabled:
color = getattr(self.theme_cls, self._text_color_str)
self.color = getattr(self.theme_cls, self._text_color_str)
else:
color = getattr(self.theme_cls, "disabled_hint_text_color")
if self.theme_cls.theme_style_switch_animation:
Animation(
color=color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self.color = color
self.color = getattr(self.theme_cls, "disabled_hint_text_color")
class MDIcon(MDFloatLayout, MDLabel):
@ -501,16 +456,11 @@ class MDIcon(MDFloatLayout, MDLabel):
and defaults to `None`.
"""
_size = ListProperty((0, 0))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Clock.schedule_once(self.adjust_size)
def adjust_size(self, *args) -> None:
def __init__(self, **kwargs):
from kivymd.uix.selectioncontrol import MDCheckbox
super().__init__(**kwargs)
if not isinstance(self, MDCheckbox):
self.size_hint = (None, None)
self._size = self.texture_size[1], self.texture_size[1]
self.size = self.texture_size
self.adaptive_size = True

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
#:import STD_INC kivymd.material_resources.STANDARD_INCREMENT
<RightContent>
@ -14,7 +14,7 @@
<MDMenu>
size_hint: None, None
width: root.width_mult * STANDARD_INCREMENT
width: root.width_mult * STD_INC
bar_width: 0
key_viewclass: "viewclass"
key_size: "height"
@ -28,7 +28,7 @@
orientation: "vertical"
<MenuContainer@MDCard>
<MenuContainer@MDCard+FakeRectangularElevationBehavior>
<MDDropdownMenu>

View File

@ -781,7 +781,7 @@ class MDDropdownMenu(ThemableBehavior, FloatLayout):
and defaults to `'[dp(7)]'`.
"""
elevation = NumericProperty(4)
elevation = NumericProperty(10)
"""
Elevation value of menu dialog.
@ -790,7 +790,7 @@ class MDDropdownMenu(ThemableBehavior, FloatLayout):
.. code-block:: python
self.menu = MDDropdownMenu(
elevation=4,
elevation=16,
...,
)
@ -798,7 +798,7 @@ class MDDropdownMenu(ThemableBehavior, FloatLayout):
:align: center
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `4`.
and defaults to `10`.
"""
_start_coords = []

View File

@ -61,7 +61,7 @@ A simple example
MDTopAppBar:
title: "Navigation Drawer"
elevation: 4
elevation: 10
pos_hint: {"top": 1}
md_bg_color: "#e7e4c0"
specific_text_color: "#4a4939"
@ -115,7 +115,7 @@ A simple example
MDScreen(
MDTopAppBar(
title="Navigation Drawer",
elevation=4,
elevation=10,
pos_hint={"top": 1},
md_bg_color="#e7e4c0",
specific_text_color="#4a4939",
@ -188,7 +188,7 @@ Standard content for the navigation bar
MDTopAppBar:
title: "Navigation Drawer"
elevation: 4
elevation: 10
pos_hint: {"top": 1}
md_bg_color: "#e7e4c0"
specific_text_color: "#4a4939"
@ -296,7 +296,7 @@ Standard content for the navigation bar
MDScreen(
MDTopAppBar(
title="Navigation Drawer",
elevation=4,
elevation=10,
pos_hint={"top": 1},
md_bg_color="#e7e4c0",
specific_text_color="#4a4939",
@ -396,7 +396,7 @@ Switching screens in the ``ScreenManager`` and using the common ``MDTopAppBar``
MDTopAppBar:
pos_hint: {"top": 1}
elevation: 4
elevation: 10
title: "MDNavigationDrawer"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
@ -465,7 +465,7 @@ Switching screens in the ``ScreenManager`` and using the common ``MDTopAppBar``
MDScreen(
MDTopAppBar(
pos_hint={"top": 1},
elevation=4,
elevation=10,
title="MDNavigationDrawer",
left_action_items=[["menu", lambda x: self.nav_drawer_open()]],
),
@ -551,9 +551,14 @@ from kivy.properties import (
StringProperty,
VariableListProperty,
)
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager
from kivymd import uix_path
from kivymd.uix.behaviors import (
DeclarativeBehavior,
FakeRectangularElevationBehavior,
)
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import MDCard
@ -1024,7 +1029,7 @@ class MDNavigationDrawerMenu(MDScrollView):
widget.text_color = widget._text_color
class MDNavigationDrawer(MDCard):
class MDNavigationDrawer(MDCard, FakeRectangularElevationBehavior):
type = OptionProperty("modal", options=("standard", "modal"))
"""
Type of drawer. Modal type will be on top of screen. Standard type will be

View File

@ -29,86 +29,45 @@ Usage
MDNavigationRailItem:
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDBoxLayout:
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Git"
icon: "git"
MDScreen:
'''
KV = '''
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
MDBoxLayout:
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Git"
icon: "git"
MDScreen:
'''
Example().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.navigationrail import MDNavigationRail, MDNavigationRailItem
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDBoxLayout(
MDNavigationRail(
MDNavigationRailItem(
text="Python",
icon="language-python",
),
MDNavigationRailItem(
text="JavaScript",
icon="language-javascript",
),
MDNavigationRailItem(
text="CPP",
icon="language-cpp",
),
MDNavigationRailItem(
text="Git",
icon="git",
),
)
)
)
Example().run()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
:align: center
@ -116,412 +75,202 @@ Usage
Example
=======
.. tabs::
.. code-block:: python
.. tab:: Declarative KV and imperative python styles
from kivy.clock import Clock
from kivy.lang import Builder
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatIconButton
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
from kivy.clock import Clock
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import CommonElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatIconButton
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
KV = '''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
KV = '''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<ExtendedButton>
elevation: 3.5
shadow_radius: 12
shadow_softness: 4
-height: "56dp"
<ExtendedButton>
elevation: 3
-height: "56dp"
<DrawerClickableItem@MDNavigationDrawerItem>
focus_color: "#e7e4c0"
unfocus_color: "#fffcf4"
<DrawerClickableItem@MDNavigationDrawerItem>
focus_color: "#e7e4c0"
unfocus_color: "#fffcf4"
MDScreen:
MDScreen:
MDNavigationLayout:
MDNavigationLayout:
ScreenManager:
ScreenManager:
MDScreen:
MDScreen:
MDBoxLayout:
orientation: "vertical"
MDBoxLayout:
adaptive_height: True
md_bg_color: "#fffcf4"
padding: "12dp"
MDLabel:
text: "12:00"
adaptive_height: True
pos_hint: {"center_y": .5}
MDBoxLayout:
MDNavigationRail:
id: navigation_rail
md_bg_color: "#fffcf4"
selected_color_background: "#e7e4c0"
ripple_color_item: "#e7e4c0"
on_item_release: app.switch_screen(*args)
MDNavigationRailMenuButton:
on_release: nav_drawer.set_state("open")
MDNavigationRailFabButton:
md_bg_color: "#b0f0d6"
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Swift"
icon: "language-swift"
ScreenManager:
id: screen_manager
transition:
FadeTransition(duration=.2, clearcolor=app.theme_cls.bg_dark)
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
md_bg_color: "#fffcf4"
elevation: 4
width: "240dp"
MDNavigationDrawerMenu:
MDBoxLayout:
orientation: "vertical"
MDBoxLayout:
orientation: "vertical"
adaptive_height: True
spacing: "12dp"
padding: "3dp", 0, 0, "12dp"
md_bg_color: "#fffcf4"
padding: "12dp"
MDIconButton:
icon: "menu"
MDLabel:
text: "12:00"
adaptive_height: True
pos_hint: {"center_y": .5}
ExtendedButton:
text: "Compose"
icon: "pencil"
MDBoxLayout:
DrawerClickableItem:
text: "Python"
icon: "language-python"
MDNavigationRail:
id: navigation_rail
md_bg_color: "#fffcf4"
selected_color_background: "#e7e4c0"
ripple_color_item: "#e7e4c0"
on_item_release: app.switch_screen(*args)
DrawerClickableItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailMenuButton:
on_release: nav_drawer.set_state("open")
DrawerClickableItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailFabButton:
md_bg_color: "#b0f0d6"
DrawerClickableItem:
text: "Swift"
icon: "language-swift"
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Swift"
icon: "language-swift"
ScreenManager:
id: screen_manager
transition:
FadeTransition(duration=.2, clearcolor=app.theme_cls.bg_dark)
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
md_bg_color: "#fffcf4"
elevation: 12
width: "240dp"
MDNavigationDrawerMenu:
MDBoxLayout:
orientation: "vertical"
adaptive_height: True
spacing: "12dp"
padding: 0, 0, 0, "12dp"
MDIconButton:
icon: "menu"
ExtendedButton:
text: "Compose"
icon: "pencil"
DrawerClickableItem:
text: "Python"
icon: "language-python"
DrawerClickableItem:
text: "JavaScript"
icon: "language-javascript"
DrawerClickableItem:
text: "CPP"
icon: "language-cpp"
DrawerClickableItem:
text: "Swift"
icon: "language-swift"
'''
class ExtendedButton(
RoundedRectangularElevationBehavior, MDFillRoundFlatIconButton
):
'''
Implements a button of type
`Extended FAB <https://m3.material.io/components/extended-fab/overview>`_.
.. rubric::
Extended FABs help people take primary actions.
They're wider than FABs to accommodate a text label and larger target
area.
This type of buttons is not yet implemented in the standard widget set
of the KivyMD library, so we will implement it ourselves in this class.
'''
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.padding = "16dp"
Clock.schedule_once(self.set_spacing)
def set_spacing(self, interval):
self.ids.box.spacing = "12dp"
def set_radius(self, *args):
if self.rounded_button:
self._radius = self.radius = self.height / 4
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def switch_screen(
self, instance_navigation_rail, instance_navigation_rail_item
):
'''
Called when tapping on rail menu items. Switches application screens.
'''
class ExtendedButton(MDFillRoundFlatIconButton, CommonElevationBehavior):
'''
Implements a button of type
`Extended FAB <https://m3.material.io/components/extended-fab/overview>`_.
.. rubric::
Extended FABs help people take primary actions.
They're wider than FABs to accommodate a text label and larger target
area.
This type of buttons is not yet implemented in the standard widget set
of the KivyMD library, so we will implement it ourselves in this class.
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.padding = "16dp"
Clock.schedule_once(self.set_spacing)
def set_spacing(self, interval):
self.ids.box.spacing = "12dp"
def set_radius(self, *args):
if self.rounded_button:
self._radius = self.radius = self.height / 4
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def switch_screen(
self, instance_navigation_rail, instance_navigation_rail_item
):
'''
Called when tapping on rail menu items. Switches application screens.
'''
self.root.ids.screen_manager.current = (
instance_navigation_rail_item.icon.split("-")[1].lower()
)
def on_start(self):
'''Creates application screens.'''
navigation_rail_items = self.root.ids.navigation_rail.get_items()[:]
navigation_rail_items.reverse()
for widget in navigation_rail_items:
name_screen = widget.icon.split("-")[1].lower()
screen = MDScreen(
name=name_screen,
md_bg_color="#edd769",
radius=[18, 0, 0, 0],
)
box = MDBoxLayout(padding="12dp")
label = MDLabel(
text=name_screen.capitalize(),
font_style="H1",
halign="right",
adaptive_height=True,
shorten=True,
)
box.add_widget(label)
screen.add_widget(box)
self.root.ids.screen_manager.add_widget(screen)
Example().run()
.. tab:: Declarative python style
.. code-block:: python
from kivy.clock import Clock
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.behaviors import CommonElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatIconButton, MDIconButton
from kivymd.uix.label import MDLabel
from kivymd.uix.navigationdrawer import (
MDNavigationDrawerItem,
MDNavigationLayout,
MDNavigationDrawer,
MDNavigationDrawerMenu,
self.root.ids.screen_manager.current = (
instance_navigation_rail_item.icon.split("-")[1].lower()
)
from kivymd.uix.navigationrail import (
MDNavigationRail,
MDNavigationRailMenuButton,
MDNavigationRailFabButton,
MDNavigationRailItem,
)
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
def on_start(self):
'''Creates application screens.'''
navigation_rail_items = self.root.ids.navigation_rail.get_items()[:]
navigation_rail_items.reverse()
for widget in navigation_rail_items:
name_screen = widget.icon.split("-")[1].lower()
screen = MDScreen(
name=name_screen,
md_bg_color="#edd769",
radius=[18, 0, 0, 0],
)
box = MDBoxLayout(padding="12dp")
label = MDLabel(
text=name_screen.capitalize(),
font_style="H1",
halign="right",
adaptive_height=True,
shorten=True,
)
box.add_widget(label)
screen.add_widget(box)
self.root.ids.screen_manager.add_widget(screen)
class DrawerClickableItem(MDNavigationDrawerItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.focus_color = "#e7e4c0"
self.unfocus_color = self.theme_cls.bg_light
self.radius = 24
class ExtendedButton(MDFillRoundFlatIconButton, CommonElevationBehavior):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.padding = "16dp"
self.elevation = 3.5
self.shadow_radius = 12
self.shadow_softness = 4
self.height = dp(56)
Clock.schedule_once(self.set_spacing)
def set_spacing(self, interval):
self.ids.box.spacing = "12dp"
def set_radius(self, *args):
if self.rounded_button:
self._radius = self.radius = self.height / 4
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.primary_palette = "Orange"
return MDScreen(
MDNavigationLayout(
MDScreenManager(
MDScreen(
MDBoxLayout(
MDBoxLayout(
MDLabel(
text="12:00",
adaptive_height=True,
pos_hint={"center_y": 0.5},
),
adaptive_height=True,
md_bg_color="#fffcf4",
padding="12dp",
),
MDBoxLayout(
MDNavigationRail(
MDNavigationRailMenuButton(
on_release=self.open_nav_drawer,
),
MDNavigationRailFabButton(
md_bg_color="#b0f0d6",
),
MDNavigationRailItem(
text="Python",
icon="language-python",
),
MDNavigationRailItem(
text="JavaScript",
icon="language-javascript",
),
MDNavigationRailItem(
text="CPP",
icon="language-cpp",
),
MDNavigationRailItem(
text="Swift",
icon="language-swift",
),
id="navigation_rail",
md_bg_color="#fffcf4",
selected_color_background="#e7e4c0",
ripple_color_item="#e7e4c0",
),
MDScreenManager(
id="screen_manager_content",
),
id="root_box",
),
id="box_rail",
orientation="vertical",
),
id="box",
),
id="screen",
),
id="screen_manager",
),
MDNavigationDrawer(
MDNavigationDrawerMenu(
MDBoxLayout(
MDIconButton(
icon="menu",
),
ExtendedButton(
text="Compose",
icon="pencil",
),
orientation="vertical",
adaptive_height=True,
spacing="12dp",
padding=("3dp", 0, 0, "12dp"),
),
DrawerClickableItem(
text="Python",
icon="language-python",
),
DrawerClickableItem(
text="JavaScript",
icon="language-javascript",
),
DrawerClickableItem(
text="CPP",
icon="language-cpp",
),
DrawerClickableItem(
text="Swift",
icon="language-swift",
),
),
id="nav_drawer",
radius=(0, 16, 16, 0),
elevation=4,
width="240dp",
),
)
def switch_screen(self, *args, screen_manager_content=None):
'''
Called when tapping on rail menu items. Switches application screens.
'''
instance_navigation_rail, instance_navigation_rail_item = args
screen_manager_content.current = (
instance_navigation_rail_item.icon.split("-")[1].lower()
)
def open_nav_drawer(self, *args):
self.root.ids.nav_drawer.set_state("open")
def on_start(self):
'''Creates application screens.'''
screen_manager = self.root.ids.screen_manager
root_box = screen_manager.ids.screen.ids.box.ids.box_rail.ids.root_box
navigation_rail = root_box.ids.navigation_rail
screen_manager_content = root_box.ids.screen_manager_content
navigation_rail_items = navigation_rail.get_items()[:]
navigation_rail_items.reverse()
navigation_rail.bind(
on_item_release=lambda *args: self.switch_screen(
*args, screen_manager_content=screen_manager_content
)
)
for widget in navigation_rail_items:
name_screen = widget.icon.split("-")[1].lower()
screen_manager_content.add_widget(
MDScreen(
MDBoxLayout(
MDLabel(
text=name_screen.capitalize(),
font_style="H1",
halign="right",
adaptive_height=True,
shorten=True,
),
padding="12dp",
),
name=name_screen,
md_bg_color="#edd769",
radius=[18, 0, 0, 0],
),
)
Example().run()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-example.gif
:align: center
@ -557,11 +306,12 @@ from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import ScaleBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
from kivymd.uix.card import MDCard
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.templates import ScaleWidget
from kivymd.uix.widget import MDWidget
with open(
@ -583,7 +333,7 @@ class PanelItems(MDBoxLayout):
"""Box for menu items."""
class RippleWidget(MDWidget, ScaleBehavior):
class RippleWidget(MDWidget, ScaleWidget):
"""
Implements a background color for a menu item -
(:class:`~MDNavigationRailItem`).
@ -812,7 +562,7 @@ class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
self.navigation_rail.dispatch("on_item_release", self)
class MDNavigationRail(MDCard):
class MDNavigationRail(MDCard, FakeRectangularElevationBehavior):
"""
:Events:
:attr:`on_item_press`

View File

@ -11,110 +11,65 @@ Components/DatePicker
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/picker-previous.png
:align: center
.. warning:: The widget is under testing. Therefore, we would be grateful if
you would let us know about the bugs found.
.. rubric:: Usage
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.pickers import MDDatePicker
from kivy.lang import Builder
KV = '''
MDFloatLayout:
from kivymd.app import MDApp
from kivymd.uix.pickers import MDDatePicker
MDTopAppBar:
title: "MDDatePicker"
pos_hint: {"top": 1}
elevation: 10
KV = '''
MDFloatLayout:
MDRaisedButton:
text: "Open date picker"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_date_picker()
'''
MDRaisedButton:
text: "Open date picker"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_date_picker()
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_save(self, instance, value, date_range):
'''
Events called when the "OK" dialog box button is clicked.
:type instance: <kivymd.uix.picker.MDDatePicker object>;
:param value: selected date;
:type value: <class 'datetime.date'>;
:param date_range: list of 'datetime.date' objects in the selected range;
:type date_range: <class 'list'>;
'''
print(instance, value, date_range)
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def on_cancel(self, instance, value):
'''Events called when the "CANCEL" dialog box button is clicked.'''
def on_save(self, instance, value, date_range):
'''
Events called when the "OK" dialog box button is clicked.
:type instance: <kivymd.uix.picker.MDDatePicker object>;
:param value: selected date;
:type value: <class 'datetime.date'>;
:param date_range: list of 'datetime.date' objects in the selected range;
:type date_range: <class 'list'>;
'''
print(instance, value, date_range)
def on_cancel(self, instance, value):
'''Events called when the "CANCEL" dialog box button is clicked.'''
def show_date_picker(self):
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save, on_cancel=self.on_cancel)
date_dialog.open()
def show_date_picker(self):
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save, on_cancel=self.on_cancel)
date_dialog.open()
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.pickers import MDDatePicker
from kivymd.uix.screen import MDScreen
Test().run()
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDRaisedButton(
text="Open data picker",
pos_hint={'center_x': .5, 'center_y': .5},
on_release=self.show_date_picker,
)
)
)
def on_save(self, instance, value, date_range):
'''
Events called when the "OK" dialog box button is clicked.
:type instance: <kivymd.uix.picker.MDDatePicker object>;
:param value: selected date;
:type value: <class 'datetime.date'>;
:param date_range: list of 'datetime.date' objects in the selected range;
:type date_range: <class 'list'>;
'''
print(instance, value, date_range)
def on_cancel(self, instance, value):
'''Events called when the "CANCEL" dialog box button is clicked.'''
def show_date_picker(self, *args):
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save, on_cancel=self.on_cancel)
date_dialog.open()
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDDatePicker.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDDatePicker.gif
:align: center
Open date dialog with the specified date
@ -126,7 +81,7 @@ Open date dialog with the specified date
date_dialog = MDDatePicker(year=1983, month=4, day=12)
date_dialog.open()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/specified-date.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/previous-date.png
:align: center
Interval date
@ -139,16 +94,12 @@ that are not included in this range will have the status `disabled`.
def show_date_picker(self):
date_dialog = MDDatePicker(
min_date=datetime.date.today(),
max_date=datetime.date(
datetime.date.today().year,
datetime.date.today().month,
datetime.date.today().day + 2,
),
min_date=datetime.date(2021, 2, 15),
max_date=datetime.date(2021, 3, 27),
)
date_dialog.open()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/range-date.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/range-date.gif
:align: center
The range of available dates can be changed in the picker dialog:
@ -171,7 +122,7 @@ You can set the range of years using the :attr:`~kivymd.uix.picker.MDDatePicker.
.. code-block:: python
def show_date_picker(self):
date_dialog = MDDatePicker(min_year=2022, max_year=2030)
date_dialog = MDDatePicker(min_year=2021, max_year=2030)
date_dialog.open()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/min-max-year-date.png
@ -190,8 +141,6 @@ Set and select a date range
:align: center
"""
from __future__ import annotations
__all__ = ("MDDatePicker", "BaseDialogPicker", "DatePickerInputField")
import calendar
@ -203,6 +152,7 @@ from typing import Union
from kivy import Logger
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
@ -225,7 +175,7 @@ from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.toast import toast
from kivymd.uix.behaviors import (
CircularRippleBehavior,
CommonElevationBehavior,
FakeRectangularElevationBehavior,
SpecificBackgroundColorBehavior,
)
from kivymd.uix.boxlayout import MDBoxLayout
@ -244,7 +194,7 @@ with open(
class BaseDialogPicker(
BaseDialog,
CommonElevationBehavior,
FakeRectangularElevationBehavior,
SpecificBackgroundColorBehavior,
):
"""
@ -305,11 +255,11 @@ class BaseDialogPicker(
primary_color = ColorProperty(None)
"""
Background color of toolbar in (r, g, b, a) or string format.
Background color of toolbar in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(primary_color="brown")
MDDatePicker(primary_color=get_color_from_hex("#72225b"))
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/primary-color-date.png
:align: center
@ -320,13 +270,13 @@ class BaseDialogPicker(
accent_color = ColorProperty(None)
"""
Background color of calendar/clock face in (r, g, b, a) or string format.
Background color of calendar/clock face in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/accent-color-date.png
@ -338,15 +288,14 @@ class BaseDialogPicker(
selector_color = ColorProperty(None)
"""
Background color of the selected day of the month or hour in (r, g, b, a)
or string format.
Background color of the selected day of the month or hour in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/selector-color-date.png
@ -358,15 +307,15 @@ class BaseDialogPicker(
text_toolbar_color = ColorProperty(None)
"""
Color of labels for text on a toolbar in (r, g, b, a) or string format.
Color of labels for text on a toolbar in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-toolbar-color-date.png
@ -378,16 +327,16 @@ class BaseDialogPicker(
text_color = ColorProperty(None)
"""
Color of text labels in calendar/clock face in (r, g, b, a) or string format.
Color of text labels in calendar/clock face in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-color-date.png
@ -399,18 +348,17 @@ class BaseDialogPicker(
text_current_color = ColorProperty(None)
"""
Color of the text of the current day of the month/hour in (r, g, b, a)
or string format.
Color of the text of the current day of the month/hour in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
text_current_color=get_color_from_hex("#e93f39"),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-current-color-date.png
@ -427,13 +375,13 @@ class BaseDialogPicker(
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
text_current_color=get_color_from_hex("#e93f39"),
text_button_color=(1, 1, 1, .5),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-button-color-date.png
@ -443,124 +391,52 @@ class BaseDialogPicker(
and defaults to `None`.
"""
input_field_background_color_normal = ColorProperty(None)
input_field_background_color = ColorProperty(None)
"""
Background color normal of input fields in (r, g, b, a) or string format.
.. versionadded:: 1.1.0
Background color of input fields in (r, g, b, a) format.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
input_field_background_color_normal="coral",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
text_current_color=get_color_from_hex("#e93f39"),
input_field_background_color=(1, 1, 1, 0.2),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-field-background-color-date.png
:align: center
:attr:`input_field_background_color_normal` is an :class:`~kivy.properties.ColorProperty`
:attr:`input_field_background_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
input_field_background_color_focus = ColorProperty(None)
"""
Background color normal of input fields in (r, g, b, a) or string format.
.. versionadded:: 1.1.0
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
input_field_background_color_normal="coral",
input_field_background_color_focus="red",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-field-background-color-focus-date.png
:align: center
:attr:`input_field_background_color_focus` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
input_field_background_color = ColorProperty(None)
"""
.. deprecated:: 1.1.0
Use :attr:`input_field_background_color_normal` instead.
"""
input_field_text_color = ColorProperty(None)
"""
.. deprecated:: 1.1.0
Use :attr:`input_field_text_color_normal` instead.
"""
Text color of input fields in (r, g, b, a) format.
input_field_text_color_normal = ColorProperty(None)
"""
Text color normal of input fields in (r, g, b, a) or string format.
.. versionadded:: 1.1.0
Background color of input fields.
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
input_field_background_color_normal="brown",
input_field_background_color_focus="red",
input_field_text_color_normal="white",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
text_current_color=get_color_from_hex("#e93f39"),
input_field_background_color=(1, 1, 1, 0.2),
input_field_text_color=(1, 1, 1, 1),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-field-text-color-normal-date.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-field-background-color-date.png
:align: center
:attr:`input_field_text_color_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
input_field_text_color_focus = ColorProperty(None)
"""
Text color focus of input fields in (r, g, b, a) or string format.
.. versionadded:: 1.1.0
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
input_field_background_color_normal="brown",
input_field_background_color_focus="red",
input_field_text_color_normal="white",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-field-text-color-normal-date.png
:align: center
:attr:`input_field_text_color_focus` is an :class:`~kivy.properties.ColorProperty`
:attr:`input_field_text_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
@ -571,18 +447,16 @@ class BaseDialogPicker(
.. code-block:: python
MDDatePicker(
primary_color="brown",
accent_color="darkred",
selector_color="red",
text_toolbar_color="lightgrey",
text_color="orange",
text_current_color="white",
text_button_color="lightgrey",
input_field_background_color_normal="brown",
input_field_background_color_focus="red",
input_field_text_color_normal="white",
input_field_text_color_focus="lightgrey",
font_name="nasalization.ttf",
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
selector_color=get_color_from_hex("#e93f39"),
text_toolbar_color=get_color_from_hex("#cccccc"),
text_color=("#ffffff"),
text_current_color=get_color_from_hex("#e93f39"),
input_field_background_color=(1, 1, 1, 0.2),
input_field_text_color=(1, 1, 1, 1),
font_name="Weather.ttf",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/font-name-date.png
@ -597,20 +471,6 @@ class BaseDialogPicker(
self.register_event_type("on_save")
self.register_event_type("on_cancel")
def on_input_field_background_color(
self, instance, value: str | list | tuple
) -> None:
"""For supported of current API."""
self.input_field_background_color_normal = value
def on_input_field_text_color(
self, instance, value: str | list | tuple
) -> None:
"""For supported of current API."""
self.input_field_text_color_normal = value
def on_save(self, *args) -> None:
"""Events called when the "OK" dialog box button is clicked."""
@ -746,13 +606,6 @@ class DatePickerDaySelectableItem(
self.owner.set_selected_widget(self)
def on_touch_down(self, touch):
# If year_layout is active don't dispatch on_touch_down events,
# so date items don't consume touch.
if not self.owner.ids._year_layout.disabled:
return
super().on_touch_down(touch)
class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
"""Implements an item for a pick list of the year."""
@ -808,7 +661,7 @@ class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
class MDDatePicker(BaseDialogPicker):
text_weekday_color = ColorProperty(None)
"""
Text color of weekday names in (r, g, b, a) or string format.
Text color of weekday names in (r, g, b, a) format.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-date-picker-text-weekday-color.png
:align: center
@ -1347,39 +1200,19 @@ class MDDatePicker(BaseDialogPicker):
"""Creates and returns a text field object used to enter dates."""
if issubclass(self.input_field_cls, MDTextField):
text_color_focus = (
self.input_field_text_color_focus
if self.input_field_text_color_focus
else self.theme_cls.primary_color
)
text_color_normal = (
self.input_field_text_color_normal
if self.input_field_text_color_normal
else self.theme_cls.disabled_hint_text_color
)
fill_color_focus = (
self.input_field_background_color_focus
if self.input_field_background_color_focus
else self.theme_cls.bg_dark
)
fill_color_normal = (
self.input_field_background_color_normal
if self.input_field_background_color_normal
else self.theme_cls.bg_darkest
)
field = self.input_field_cls(
owner=self,
helper_text=self.helper_text,
fill_color_normal=fill_color_normal,
fill_color_focus=fill_color_focus,
hint_text_color_normal=text_color_normal,
hint_text_color_focus=text_color_focus,
text_color_normal=text_color_normal,
text_color_focus=text_color_focus,
line_color_focus=text_color_focus,
line_color_normal=text_color_normal,
line_color_normal=self.theme_cls.divider_color,
)
field.color_mode = "custom"
field.line_color_focus = (
self.theme_cls.primary_color
if not self.input_field_text_color
else self.input_field_text_color
)
field.current_hint_text_color = field.line_color_focus
field._current_hint_text_color = field.line_color_focus
return field
else:
raise TypeError(

View File

@ -16,73 +16,35 @@ Components/TimePicker
.. rubric:: Usage
.. tabs::
.. code-block::
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.pickers import MDTimePicker
from kivy.lang import Builder
KV = '''
MDFloatLayout:
from kivymd.app import MDApp
from kivymd.uix.pickers import MDTimePicker
KV = '''
MDFloatLayout:
MDRaisedButton:
text: "Open time picker"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_time_picker()
'''
MDRaisedButton:
text: "Open time picker"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_time_picker()
'''
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def show_time_picker(self):
'''Open time picker dialog.'''
def show_time_picker(self):
'''Open time picker dialog.'''
time_dialog = MDTimePicker()
time_dialog.open()
time_dialog = MDTimePicker()
time_dialog.open()
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.pickers import MDTimePicker
from kivymd.uix.screen import MDScreen
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDRaisedButton(
text="Open time picker",
pos_hint={'center_x': .5, 'center_y': .5},
on_release=self.show_time_picker,
)
)
)
def show_time_picker(self, *args):
'''Open time picker dialog.'''
MDTimePicker().open()
Test().run()
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDTimePicker.png
:align: center
@ -129,14 +91,14 @@ Use the :attr:`~MDTimePicker.set_time` method of the
.. code-block:: python
MDTimePicker(
primary_color="brown",
accent_color="red",
text_button_color="white",
).open()
time_dialog = MDTimePicker(
primary_color=get_color_from_hex("#72225b"),
accent_color=get_color_from_hex("#5d1a4a"),
text_button_color=(1, 1, 1, 1),
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-customization.png
:align: center
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-customization.png
:align: center
"""
__all__ = ("MDTimePicker",)
@ -232,8 +194,8 @@ class TimeInputTextField(MDTextField):
hour_regx = "^[0-9]$|^0[1-9]$|^1[0-2]$"
minute_regx = "^[0-9]$|^0[0-9]$|^[1-5][0-9]$"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self.set_text)
self.register_event_type("on_select")
self.bind(text_color_focus=self.setter("hint_text_color_normal"))
@ -255,20 +217,17 @@ class TimeInputTextField(MDTextField):
to somehow make them aligned.
"""
def set_text(*args):
if not self.text:
self.text = " "
if not self.text:
self.text = " "
self._refresh_text(self.text)
max_size = max(self._lines_rects, key=lambda r: r.size[0]).size
dx = (self.width - max_size[0]) / 2.0
dy = (self.height - max_size[1]) / 2.0
self.padding = [dx, dy, dx, dy]
self._refresh_text(self.text)
max_size = max(self._lines_rects, key=lambda r: r.size[0]).size
dx = (self.width - max_size[0]) / 2.0
dy = (self.height - max_size[1]) / 2.0
self.padding = [dx, dy, dx, dy]
if len(self.text) > 1:
self.text = self.text.replace(" ", "")
Clock.schedule_once(set_text)
if len(self.text) > 1:
self.text = self.text.replace(" ", "")
def on_focus(self, *args) -> None:
super().on_focus(*args)

View File

@ -36,7 +36,7 @@ Example
title: app.title
md_bg_color: app.theme_cls.primary_color
background_palette: 'Primary'
elevation: 4
elevation: 10
left_action_items: [['menu', lambda x: x]]
MDScrollViewRefreshLayout:

View File

@ -29,7 +29,7 @@ MDScreen
md_bg_color: app.theme_cls.primary_color
"""
from kivy.properties import ListProperty, ObjectProperty
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen
from kivymd.uix import MDAdaptiveWidget
@ -44,34 +44,20 @@ class MDScreen(DeclarativeBehavior, Screen, MDAdaptiveWidget):
see in the :class:`~kivy.uix.screenmanager.Screen` class documentation.
"""
hero_to = ObjectProperty(deprecated=True)
hero_to = ObjectProperty()
"""
Must be a :class:`~kivymd.uix.hero.MDHeroTo` class.
Must be a :class:`~kivymd.uix.hero.MDHeroTo` class.
See the documentation of the
`MDHeroTo <https://kivymd.readthedocs.io/en/latest/components/hero/>`_
widget for more detailed information.
.. deprecated:: 1.0.0
Use attr:`heroes_to` attribute instead.
.. versionchanged:: 1.0.0
:attr:`hero_to` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
heroes_to = ListProperty()
"""
Must be a list of :class:`~kivymd.uix.hero.MDHeroTo` class.
.. versionadded:: 1.0.0
:attr:`heroes_to` is an :class:`~kivy.properties.LiatProperty`
and defaults to `[]`.
"""
def on_hero_to(self, screen, widget: MDHeroTo) -> None:
"""Called when the value of the :attr:`hero_to` attribute changes."""
def on_hero_to(self, screen, widget) -> None:
if not isinstance(widget, MDHeroTo) or not issubclass(
widget.__class__, MDHeroTo
):
@ -79,4 +65,3 @@ class MDScreen(DeclarativeBehavior, Screen, MDAdaptiveWidget):
f"The `{widget}` widget must be an `kivymd.uix.hero.MDHeroTo` "
f"class or inherited from this class"
)
self.heroes_to = [widget]

View File

@ -10,7 +10,6 @@ If you want to use Hero animations you need to use
:class:`~kivy.uix.screenmanager.ScreenManager` class.
"""
from kivy import Logger
from kivy.clock import Clock
from kivy.properties import ListProperty, StringProperty
from kivy.uix.screenmanager import ScreenManager
@ -22,22 +21,17 @@ from kivymd.uix.hero import MDHeroFrom
class MDScreenManager(DeclarativeBehavior, ScreenManager):
"""
Screen manager. This is the main class that will control your
:class:`~kivymd.uix.screen.MDScreen` stack and memory.
For more
:class:`~kivymd.uix.screen.MDScreen` stack and memory. For more
information, see in the :class:`~kivy.uix.screenmanager.ScreenManager`
class documentation.
"""
current_hero = StringProperty(None, deprecated=True)
current_hero = StringProperty(None)
"""
The name of the current tag for the :class:`~kivymd.uix.hero.MDHeroFrom`
and :class:`~kivymd.uix.hero.MDHeroTo` objects that will be animated when
animating the transition between screens.
.. deprecated:: 1.1.0
Use :attr:`current_heroes` attribute instead.
See the `Hero <https://kivymd.readthedocs.io/en/latest/components/hero/>`_
module documentation for more information about creating and using Hero
animations.
@ -46,17 +40,6 @@ class MDScreenManager(DeclarativeBehavior, ScreenManager):
and defaults to `None`.
"""
current_heroes = ListProperty()
"""
A list of names (tags) of heroes that need to be animated when moving
to the next screen.
.. versionadded:: 1.1.0
:attr:`current_heroes` is an :class:`~kivy.properties.ListProperty`
and defaults to `[]`.
"""
# Collection of `MDHeroFrom` objects on all screens of the current
# screen manager.
_heroes_data = ListProperty()
@ -75,48 +58,28 @@ class MDScreenManager(DeclarativeBehavior, ScreenManager):
self.transition = MDSlideTransition()
def get_hero_from_widget(self) -> list:
def get_hero_from_widget(self) -> None:
"""
Get a list of :class:`~kivymd.uix.hero.MDHeroFrom` objects according
to the tag names specified in the :attr:`~current_heroes` list.
Get an :class:`~kivymd.uix.hero.MDHeroTo` object with the
:attr:`~current_hero` tag.
"""
hero_from_widget = []
hero_from_widget = None
for name_hero in self.current_heroes:
for hero_widget in self._heroes_data:
if isinstance(hero_widget, MDHeroFrom) or issubclass(
hero_widget.__class__, MDHeroFrom
):
if hero_widget.tag == name_hero:
hero_from_widget.append(hero_widget)
for hero_widget in self._heroes_data:
if isinstance(hero_widget, MDHeroFrom) or issubclass(
hero_widget.__class__, MDHeroFrom
):
if hero_widget.tag == self.current_hero:
hero_from_widget = hero_widget
break
return hero_from_widget
def on_current_hero(self, instance, value: str) -> None:
"""
Called when the value of the :attr:`current_hero` attribute changes.
"""
Logger.warning(
"KivyMD: "
"`kivymd/uix/screenmanager.MDScreenManager.current_hero` "
"attribute is deprecated. "
"Use `kivymd/uix/screenmanager.MDScreenManager.current_heroes` "
"attribute instead."
)
if value:
self.current_heroes = [value]
else:
self.current_heroes = []
def add_widget(self, widget, *args, **kwargs):
super().add_widget(widget, *args, **kwargs)
Clock.schedule_once(lambda x: self._create_heroes_data(widget))
# TODO: Add a method to delete an object from the arrt:`_heroes_data`
# collection when deleting an object using the `remove_widget` method.
def _create_heroes_data(self, widget):
def find_hero_widget(child_widget):
widget_hero = None

View File

@ -15,11 +15,8 @@
pos_hint: {"center_y": .5}
x: root._segment_switch_x
md_bg_color: root.segment_color
elevation: 2
elevation: 6
_radius: root.radius[0] - 4
width:
segment_panel.width / segment_panel.children_number \
- segment_panel.spacing
SegmentPanel:
id: segment_panel

View File

@ -10,77 +10,58 @@ Components/SegmentedControl
Usage
=====
.. tabs::
.. code-block:: python
.. tab:: Declarative KV style
from kivy.lang import Builder
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.app import MDApp
KV = '''
MDScreen:
KV = '''
MDScreen:
MDSegmentedControl:
pos_hint: {"center_x": .5, "center_y": .5}
MDSegmentedControl:
pos_hint: {"center_x": .5, "center_y": .5}
MDSegmentedControlItem:
text: "Male"
MDSegmentedControlItem:
text: "Male"
MDSegmentedControlItem:
text: "Female"
MDSegmentedControlItem:
text: "Female"
MDSegmentedControlItem:
text: "All"
'''
MDSegmentedControlItem:
text: "All"
'''
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Example().run()
Test().run()
.. tab:: Declarative python style
Or only in python code:
.. code-block:: python
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.segmentedcontrol import (
MDSegmentedControl, MDSegmentedControlItem
)
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.segmentedcontrol import MDSegmentedControl, MDSegmentedControlItem
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDSegmentedControl(
MDSegmentedControlItem(
text="Male"
),
MDSegmentedControlItem(
text="Female"
),
MDSegmentedControlItem(
text="All"
),
pos_hint={"center_x": 0.5, "center_y": 0.5}
)
)
)
class Test(MDApp):
def build(self):
screen = MDScreen()
segment_control = MDSegmentedControl(pos_hint={"center_x": .5, "center_y": .5})
segment_control.add_widget(MDSegmentedControlItem(text="Male"))
segment_control.add_widget(MDSegmentedControlItem(text="Female"))
segment_control.add_widget(MDSegmentedControlItem(text="All"))
screen.add_widget(segment_control)
return screen
Example().run()
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-segmented-control-usage.gif
:align: center
@ -136,22 +117,12 @@ with open(
class MDSegmentedControlItem(MDLabel):
"""
Implements a label to place on the :class:`~SegmentPanel` panel.
See :class:`~kivymd.uix.label.MDLabel` class documentation for more
information.
"""
"""Implements a label to place on the :class:`~SegmentPanel` panel."""
# TODO: Add an attribute for the color of the active segment label.
class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
"""
Implements a segmented control panel.
Relative layout class. For more information, see in the
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
:Events:
`on_active`
Called when the segment is activated.
@ -164,7 +135,7 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
.. code-block:: kv
MDSegmentedControl:
md_bg_color: "brown"
md_bg_color: "#451938"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-segmented-control-md-bg-color.png
:align: center
@ -180,8 +151,8 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
.. code-block:: kv
MDSegmentedControl:
md_bg_color: "brown"
segment_color: "red"
md_bg_color: "#451938"
segment_color: "#e4514f"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-segmented-control-segment-color.png
:align: center
@ -189,8 +160,8 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
.. code-block:: kv
MDSegmentedControl:
md_bg_color: "brown"
segment_color: "red"
md_bg_color: "#451938"
segment_color: "#e4514f"
MDSegmentedControlItem:
text: "[color=fff]Male[/color]"
@ -225,9 +196,9 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
.. code-block:: kv
MDSegmentedControl:
md_bg_color: "brown"
segment_color: "red"
separator_color: "white"
md_bg_color: "#451938"
segment_color: "#e4514f"
separator_color: 1, 1, 1, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-segmented-control-separator-color.png
:align: center
@ -284,6 +255,9 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
Clock.schedule_once(self.set_default_colors)
Clock.schedule_once(self._remove_last_separator)
# FIXME: Sometimes this interval is not enough to get the width
# of the segment label textures.
Clock.schedule_once(self._set_width_segment_switch, 2.2)
def set_default_colors(self, *args) -> None:
"""
@ -339,10 +313,6 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
self.ids.segment_panel.add_widget(widget)
separator = MDSeparator(orientation="vertical")
self.ids.segment_panel.add_widget(separator)
if not self.ids.segment_panel._started:
self.ids.segment_panel._started = True
else:
self.ids.segment_panel.children_number += 1
Clock.schedule_once(
lambda x: self.update_separator_color(separator)
)
@ -356,6 +326,15 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
self.current_active_segment = widget
self.dispatch("on_active", widget)
def _set_width_segment_switch(self, *args):
"""
Sets the width of the switch. I think this is not done quite correctly.
"""
self.ids.segment_switch.width = self.ids.segment_panel.children[
0
].width + dp(12)
def _remove_last_separator(self, *args):
self.ids.segment_panel.remove_widget(self.ids.segment_panel.children[0])
@ -371,7 +350,3 @@ class SegmentPanel(MDBoxLayout):
Implements a panel for placing items - :class:`~MDSegmentedControlItem`
for the :class:`~MDSegmentedControl` class.
"""
children_number = NumericProperty(1)
_started = BooleanProperty(defaultvalue=False)

View File

@ -101,7 +101,7 @@
size_hint: None, None
size: dp(24), dp(24)
elevation:
(2.5 if root.active else 1) \
(8 if root.active else 5) \
if app.theme_cls.material_style != "M3" else \
0
pos:

View File

@ -192,10 +192,15 @@ from kivy.properties import (
)
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.floatlayout import FloatLayout
from kivy.utils import get_color_from_hex
from kivymd import uix_path
from kivymd.color_definitions import colors
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import CircularRippleBehavior, CommonElevationBehavior
from kivymd.uix.behaviors import (
CircularRippleBehavior,
FakeCircularElevationBehavior,
)
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.label import MDIcon
@ -356,10 +361,8 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
disabled=self.update_color,
state=self.update_color,
)
self.theme_cls.bind(
theme_style=self.update_primary_color,
primary_color=self.update_primary_color,
)
self.theme_cls.bind(primary_color=self.update_primary_color)
self.theme_cls.bind(theme_style=self.update_primary_color)
self.update_icon()
self.update_color()
@ -420,7 +423,7 @@ class ThumbIcon(MDIcon):
class Thumb(
CommonElevationBehavior,
FakeCircularElevationBehavior,
CircularRippleBehavior,
MDFloatLayout,
):

View File

@ -3,7 +3,7 @@
#:import colors kivymd.color_definitions.colors
<HintBoxContainer@MDCard>
<HintBoxContainer@MDCard+FakeRectangularElevationBehavior>
<MDSlider>
@ -131,20 +131,16 @@
) \
) \
)
elevation: 0 if root._is_off else (3 if root.active else 1)
elevation: 0 if root._is_off else (4 if root.active else 2)
HintBoxContainer:
id: hint_box
size_hint: None, None
md_bg_color: root.hint_bg_color if root.hint_bg_color else [0, 0, 0, 0]
elevation: 1.5
md_bg_color: root.hint_bg_color
elevation: 0
opacity: 1 if root.active else 0
radius: root.hint_radius
padding: "6dp", "6dp", "6dp", "8dp"
shadow_color:
([0, 0, 0, 0.6] if root.hint_bg_color else [0, 0, 0, 0]) \
if root.active else \
[0, 0, 0, 0]
size:
lbl_value.width + self.padding[0] * 2, \
lbl_value.height + self.padding[0]

View File

@ -82,7 +82,7 @@ class MDSlider(ThemableBehavior, Slider):
and defaults to `True`.
"""
hint_bg_color = ColorProperty(None)
hint_bg_color = ColorProperty([0, 0, 0, 0])
"""
Hint rectangle color in (r.g.b.a) format.

View File

@ -38,8 +38,8 @@ Example
from kivy.lang.builder import Builder
from kivymd.app import MDApp
from kivymd.uix.card import MDCard
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
KV = '''
<CardItem>
@ -47,6 +47,7 @@ Example
height: "86dp"
padding: "4dp"
radius: 12
elevation: 4
FitImage:
source: "avatar.jpg"
@ -94,10 +95,8 @@ Example
'''
class CardItem(MDCard):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.elevation = 3
class CardItem(MDCard, RoundedRectangularElevationBehavior):
pass
class Example(MDApp):
@ -193,6 +192,7 @@ class MDSliverAppbar(MDBoxLayout, ThemableBehavior):
from kivymd.uix.card import MDCard
from kivymd.uix.toolbar import MDTopAppBar
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
KV = '''
#:import SliverToolbar __main__.SliverToolbar
@ -203,6 +203,7 @@ class MDSliverAppbar(MDBoxLayout, ThemableBehavior):
height: "86dp"
padding: "4dp"
radius: 12
elevation: 4
FitImage:
source: "avatar.jpg"
@ -251,16 +252,13 @@ class MDSliverAppbar(MDBoxLayout, ThemableBehavior):
'''
class CardItem(MDCard):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.elevation = 3
class CardItem(MDCard, RoundedRectangularElevationBehavior):
pass
class SliverToolbar(MDTopAppBar):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.shadow_color = (0, 0, 0, 0)
self.type_height = "medium"
self.headline_text = "Headline medium"
self.left_action_items = [["arrow-left", lambda x: x]]
@ -424,7 +422,6 @@ class MDSliverAppbar(MDBoxLayout, ThemableBehavior):
# Adding a custom MDTopAppBar object.
if issubclass(instance_toolbar_cls.__class__, MDTopAppBar):
instance_toolbar_cls.pos_hint = {"top": 1}
instance_toolbar_cls.elevation = 0
self.ids.float_box.add_widget(instance_toolbar_cls)
else:
raise MDSliverAppbarException(

View File

@ -8,7 +8,7 @@
padding: "10dp", "10dp", "10dp", "10dp"
md_bg_color: "323232" if not root.bg_color else root.bg_color
radius: root.radius
elevation: 4 if root.padding else 0
elevation: 11 if root.padding else 0
canvas:
Color:

View File

@ -284,6 +284,7 @@ from kivy.properties import (
)
from kivymd import uix_path
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.button import BaseButton
from kivymd.uix.card import MDCard
@ -293,7 +294,7 @@ with open(
Builder.load_string(kv_file.read())
class BaseSnackbar(MDCard):
class BaseSnackbar(MDCard, FakeRectangularElevationBehavior):
"""
:Events:
:attr:`on_open`

View File

@ -38,7 +38,7 @@ Example
MDTopAppBar:
id: toolbar
title: "MDSwiper"
elevation: 4
elevation: 10
pos_hint: {"top": 1}
MDSwiper:
@ -142,7 +142,7 @@ Example
MDTopAppBar:
id: toolbar
title: "MDSwiper"
elevation: 4
elevation: 10
pos_hint: {"top": 1}
MDSwiper:
@ -203,6 +203,7 @@ from kivy.animation import Animation
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.effects.dampedscroll import DampedScrollEffect
from kivy.event import EventDispatcher
from kivy.lang.builder import Builder
from kivy.properties import (
BooleanProperty,
@ -211,6 +212,7 @@ from kivy.properties import (
StringProperty,
)
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.utils import platform
from kivymd import uix_path
@ -292,7 +294,7 @@ class MDSwiperItem(MDBoxLayout):
anim.start(self)
class MDSwiper(MDScrollView):
class MDSwiper(MDScrollView, EventDispatcher):
items_spacing = NumericProperty("20dp")
"""
The space between each :class:`MDSwiperItem`.

View File

@ -79,10 +79,6 @@
layout: layout
size_hint: 1, None
elevation: root.elevation
radius: root.radius
shadow_offset: root.shadow_offset
shadow_color: root.shadow_color
shadow_softness: root.shadow_softness
height: root.tab_bar_height
md_bg_color:
self.theme_cls.primary_color \

View File

@ -944,6 +944,7 @@ from kivy.properties import (
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.scrollview import ScrollView
from kivy.uix.widget import Widget
from kivy.utils import boundary
from kivymd import uix_path
@ -952,11 +953,11 @@ from kivymd.icon_definitions import md_icons
from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.uix.behaviors import (
DeclarativeBehavior,
FakeRectangularElevationBehavior,
RectangularRippleBehavior,
SpecificBackgroundColorBehavior,
)
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import MDCard
from kivymd.uix.carousel import MDCarousel
from kivymd.uix.label import MDLabel
@ -1023,7 +1024,7 @@ class MDTabsLabel(ToggleButtonBehavior, RectangularRippleBehavior, MDLabel):
Clock.schedule_once(self.tab_bar._label_request_indicator_update, 0)
class MDTabsBase:
class MDTabsBase(Widget):
"""
This class allow you to create a tab.
You must create a new class that inherits from MDTabsBase.
@ -1129,9 +1130,9 @@ class MDTabsBase:
This property will affect the Tab's Title Label widget.
"""
def __init__(self, *args, **kwargs):
def __init__(self, **kwargs):
self.tab_label = MDTabsLabel(tab=self)
super().__init__(*args, **kwargs)
super().__init__(**kwargs)
self.bind(
icon=self._update_text,
title=self._update_text,
@ -1272,7 +1273,9 @@ class MDTabsScrollView(ScrollView):
_update(self.effect_y, scroll_y)
class MDTabsBar(MDCard):
class MDTabsBar(
ThemableBehavior, FakeRectangularElevationBehavior, MDBoxLayout
):
"""
This class is just a boxlayout that contains the scroll view for tabs.
It is also responsible for resizing the tab shortcut when necessary.
@ -1548,43 +1551,13 @@ class MDTabs(
and defaults to `None`.
"""
shadow_softness = NumericProperty(12)
"""
See :attr:`kivymd.uix.behaviors.CommonElevationBehavior.shadow_softness`
attribute.
.. versionadded:: 1.1.0
:attr:`shadow_softness` is an :class:`~kivy.properties.NumericProperty`
and defaults to `12`.
"""
shadow_color = ColorProperty([0, 0, 0, 0.6])
"""
See :attr:`kivymd.uix.behaviors.CommonElevationBehavior.shadow_color`
attribute.
.. versionadded:: 1.1.0
:attr:`shadow_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0.6]`.
"""
shadow_offset = ListProperty((0, 0))
"""
See :attr:`kivymd.uix.behaviors.CommonElevationBehavior.shadow_offset`
attribute.
.. versionadded:: 1.1.0
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
and defaults to `[0, 0]`.
"""
elevation = NumericProperty(0)
"""
See :attr:`kivymd.uix.behaviors.CommonElevationBehavior.elevation`
attribute.
Tab value elevation.
.. seealso::
`Behaviors/Elevation <https://kivymd.readthedocs.io/en/latest/behaviors/elevation/index.html>`_
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0`.

View File

@ -487,8 +487,6 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
_outer_radius = NumericProperty(0)
_target_radius = NumericProperty(0)
__elevation = 0
def __init__(self, **kwargs):
self.ripple_max_dist = dp(90)
self.on_outer_radius(self, self.outer_radius)
@ -516,89 +514,6 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
if not self.outer_circle_color:
self.outer_circle_color = self.theme_cls.primary_color[:-1]
def start(self, *args):
"""Starts widget opening animation."""
self._initialize()
self._animate_outer()
self.state = "open"
self.core_title_text.opacity = 1
self.core_description_text.opacity = 1
self.dispatch("on_open")
elevation = getattr(self.widget, "elevation", None)
if elevation:
self.__elevation = elevation
self.widget.elevation = 0
def stop(self, *args):
"""Starts widget close animation."""
# It needs a better implementation.
if self.anim_ripple is not None:
self.anim_ripple.unbind(on_complete=self._repeat_ripple)
self.core_title_text.opacity = 0
self.core_description_text.opacity = 0
anim = Animation(
d=0.15,
t="in_cubic",
**dict(
zip(
["_outer_radius", "_target_radius", "target_ripple_radius"],
[0, 0, 0],
)
),
)
anim.bind(on_complete=self._after_stop)
anim.start(self.widget)
def on_open(self, *args):
"""Called at the time of the start of the widget opening animation."""
def on_close(self, *args):
"""Called at the time of the start of the widget closed animation."""
def on_draw_shadow(self, instance, value):
Logger.warning(
"The shadow adding method will be implemented in future versions"
)
def on_description_text(self, instance, value):
self.core_description_text.text = value
def on_description_text_size(self, instance, value):
self.core_description_text.font_size = value
def on_description_text_bold(self, instance, value):
self.core_description_text.bold = value
def on_title_text(self, instance, value):
self.core_title_text.text = value
def on_title_text_size(self, instance, value):
self.core_title_text.font_size = value
def on_title_text_bold(self, instance, value):
self.core_title_text.bold = value
def on_outer_radius(self, instance, value):
self._outer_radius = self.outer_radius * 2
def on_target_radius(self, instance, value):
self._target_radius = self.target_radius * 2
def on_target_touch(self):
if self.stop_on_target_touch:
self.stop()
def on_outer_touch(self):
if self.stop_on_outer_touch:
self.stop()
def on_outside_click(self):
if self.cancelable:
self.stop()
def _initialize(self):
setattr(self.widget, "_outer_radius", 0)
setattr(self.widget, "_target_radius", 0)
@ -612,7 +527,7 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
def _draw_canvas(self):
_pos = self._ttv_pos()
self.widget.canvas.before.remove_group("ttv_group")
self.widget.canvas.before.clear()
with self.widget.canvas.before:
# Outer circle.
@ -673,14 +588,34 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
group="ttv_group",
)
def stop(self, *args):
"""Starts widget close animation."""
# It needs a better implementation.
if self.anim_ripple is not None:
self.anim_ripple.unbind(on_complete=self._repeat_ripple)
self.core_title_text.opacity = 0
self.core_description_text.opacity = 0
anim = Animation(
d=0.15,
t="in_cubic",
**dict(
zip(
["_outer_radius", "_target_radius", "target_ripple_radius"],
[0, 0, 0],
)
),
)
anim.bind(on_complete=self._after_stop)
anim.start(self.widget)
def _after_stop(self, *args):
self.widget.canvas.before.remove_group("ttv_group")
args[0].stop_all(self.widget)
elev = getattr(self.widget, "elevation", None)
elevation = getattr(self.widget, "elevation", None)
if elevation:
self.widget.elevation = self.__elevation
if elev:
self._fix_elev()
self.dispatch("on_close")
# Don't forget to unbind the function or it'll mess
@ -704,6 +639,16 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
)
Color(a=1)
def start(self, *args):
"""Starts widget opening animation."""
self._initialize()
self._animate_outer()
self.state = "open"
self.core_title_text.opacity = 1
self.core_description_text.opacity = 1
self.dispatch("on_open")
def _animate_outer(self):
anim = Animation(
d=0.2,
@ -739,6 +684,53 @@ class MDTapTargetView(ThemableBehavior, EventDispatcher):
setattr(self.widget, "target_ripple_alpha", 1)
self._animate_ripple()
def on_open(self, *args):
"""Called at the time of the start of the widget opening animation."""
def on_close(self, *args):
"""Called at the time of the start of the widget closed animation."""
def on_draw_shadow(self, instance, value):
Logger.warning(
"The shadow adding method will be implemented in future versions"
)
def on_description_text(self, instance, value):
self.core_description_text.text = value
def on_description_text_size(self, instance, value):
self.core_description_text.font_size = value
def on_description_text_bold(self, instance, value):
self.core_description_text.bold = value
def on_title_text(self, instance, value):
self.core_title_text.text = value
def on_title_text_size(self, instance, value):
self.core_title_text.font_size = value
def on_title_text_bold(self, instance, value):
self.core_title_text.bold = value
def on_outer_radius(self, instance, value):
self._outer_radius = self.outer_radius * 2
def on_target_radius(self, instance, value):
self._target_radius = self.target_radius * 2
def on_target_touch(self):
if self.stop_on_target_touch:
self.stop()
def on_outer_touch(self):
if self.stop_on_outer_touch:
self.stop()
def on_outside_click(self):
if self.cancelable:
self.stop()
def _some_func(self, wid, touch):
"""
This function decides which one to dispatch based on the touch

View File

@ -0,0 +1,9 @@
<RotateWidget>
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: tuple(self.rotate_value_axis)
origin: self.center
canvas.after:
PopMatrix

View File

@ -2,31 +2,127 @@
Templates/RotateWidget
======================
.. deprecated:: 1.0.0
.. versionadded:: 1.0.0
.. note:: `RotateWidget` class has been deprecated. Please use
`RotateBahavior <https://kivymd.readthedocs.io/en/latest/behaviors/rotate/>`_
class instead.
Base class for controlling the rotate of the widget.
.. note:: See `kivy.graphics.Rotate
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Rotate>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.uix.button import Button
KV = '''
Screen:
RotateButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: 0, 0, 1
origin: self.center
canvas.after:
PopMatrix
'''
class RotateButton(Button):
rotate_value_angle = NumericProperty(0)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: Button) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.templates import RotateWidget
KV = '''
MDScreen:
RotateButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
elevation:0
'''
class RotateButton(MDRaisedButton, RotateWidget):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: MDRaisedButton) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
"""
__all__ = ("RotateWidget",)
from kivy import Logger
import os
from kivymd.uix.behaviors import RotateBehavior
from kivy.lang import Builder
from kivy.properties import ListProperty, NumericProperty
from kivymd import uix_path
with open(
os.path.join(uix_path, "templates", "rotatewidget", "rotatewidget.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class RotateWidget(RotateBehavior):
class RotateWidget:
"""Base class for controlling the rotate of the widget."""
rotate_value_angle = NumericProperty(0)
"""
.. deprecated:: 1.1.0
Use :class:`~kivymd.uix.behaviors.rotate_behavior.RotateBehavior`
class instead.
Property for getting/setting the angle of the rotation.
:attr:`rotate_value_angle` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
Logger.warning(
"KivyMD: "
"The `RotateWidget` class has been deprecated. "
"Use the `RotateBehavior` class instead."
)
rotate_value_axis = ListProperty((0, 0, 1))
"""
Property for getting/setting the axis of the rotation.
:attr:`rotate_value_axis` is an :class:`~kivy.properties.NumericProperty`
and defaults to `(0, 0, 1)`.
"""

View File

@ -0,0 +1,10 @@
<ScaleWidget>
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix

View File

@ -2,33 +2,149 @@
Templates/ScaleWidget
=====================
.. deprecated:: 1.1.0
.. versionadded:: 1.0.0
Base class for controlling the scale of the widget.
.. note:: `ScaleWidget` class has been deprecated. Please use
`ScaleBehavior <https://kivymd.readthedocs.io/en/latest/behaviors/scale/>`_
class instead.
.. note:: See `kivy.graphics.Scale
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Scale>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.button import Button
from kivy.app import App
KV = '''
Screen:
ScaleButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix
'''
class ScaleButton(Button):
scale_value_x = NumericProperty(1)
scale_value_y = NumericProperty(1)
scale_value_z = NumericProperty(1)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: Button) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.templates import ScaleWidget
KV = '''
MDScreen:
ScaleButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
elevation:0
'''
class ScaleButton(MDRaisedButton, ScaleWidget):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: MDRaisedButton) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
"""
__all__ = ("ScaleWidget",)
from kivy import Logger
import os
from kivymd.uix.behaviors import ScaleBehavior
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivymd import uix_path
with open(
os.path.join(uix_path, "templates", "scalewidget", "scalewidget.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class ScaleWidget(ScaleBehavior):
class ScaleWidget:
"""Base class for controlling the scale of the widget."""
scale_value_x = NumericProperty(1)
"""
.. deprecated:: 1.1.0
Use :class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior`
class instead.
X-axis value.
:attr:`scale_value_x` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
Logger.warning(
"KivyMD: "
"The `ScaleWidget` class has been deprecated. "
"Use the `ScaleBehavior` class instead."
)
scale_value_y = NumericProperty(1)
"""
Y-axis value.
:attr:`scale_value_y` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
scale_value_z = NumericProperty(1)
"""
Z-axis value.
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""

View File

@ -0,0 +1,19 @@
<StencilWidget>
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilPop

View File

@ -2,33 +2,115 @@
Templates/StencilWidget
=======================
.. deprecated:: 1.1.0
.. versionadded:: 1.0.0
Base class for controlling the stencil instructions of the widget.
.. note:: `StencilWidget` class has been deprecated. Please use
`StencilBehavior <https://kivymd.readthedocs.io/en/latest/behaviors/stencil/>`_
class instead.
.. note:: See `Stencil instructions
<https://kivy.org/doc/stable/api-kivy.graphics.stencil_instructions.html>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.lang import Builder
from kivy.app import App
KV = '''
Carousel:
Button:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
StencilPop
'''
class Test(App):
def build(self):
return Builder.load_string(KV)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.templates import StencilWidget
from kivymd.uix.fitimage import FitImage
KV = '''
MDCarousel:
StencilImage:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
source: "image.png"
'''
class StencilImage(FitImage, StencilWidget):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Test().run()
"""
__all__ = ("StencilWidget",)
from kivy import Logger
import os
from kivymd.uix.behaviors import StencilBehavior
from kivy.lang import Builder
from kivy.properties import VariableListProperty
from kivymd import uix_path
with open(
os.path.join(uix_path, "templates", "stencilwidget", "stencilwidget.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class StencilWidget(StencilBehavior):
"""
.. deprecated:: 1.1.0
Use :class:`~kivymd.uix.behaviors.scale_behavior.StencilBehavior`
class instead.
class StencilWidget:
"""Base class for controlling the stencil instructions of the widget"""
radius = VariableListProperty([0], length=4)
"""
Canvas radius.
def __init__(self, **kwargs):
super().__init__(**kwargs)
Logger.warning(
"KivyMD: "
"The `StencilWidget` class has been deprecated. "
"Use the `StencilBehavior` class instead."
)
.. versionadded:: 1.0.0
.. code-block:: python
# Top left corner slice.
MDWidget:
radius: [25, 0, 0, 0]
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[0, 0, 0, 0]`.
"""

View File

@ -1,7 +1,4 @@
<MDTextField>
input_filter: self.field_filter
do_backspace: self.do_backspace
canvas.before:
Clear

View File

@ -14,6 +14,7 @@ Components/TextField
`KivyMD` provides the following field classes for use:
- MDTextField_
- MDTextFieldRound_
- MDTextFieldRect_
.. Note:: :class:`~MDTextField` inherited from
@ -78,15 +79,15 @@ parameter to `True`:
from kivymd.app import MDApp
KV = '''
MDScreen:
BoxLayout:
padding: "10dp"
MDTextField:
id: text_field_error
hint_text: "Helper text on error (press 'Enter')"
helper_text: "There will always be a mistake"
helper_text_mode: "on_error"
pos_hint: {"center_x": .5, "center_y": .5}
size_hint_x: .5
pos_hint: {"center_y": .5}
'''
@ -96,8 +97,6 @@ parameter to `True`:
self.screen = Builder.load_string(KV)
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
self.screen.ids.text_field_error.bind(
on_text_validate=self.set_error_message,
on_focus=self.set_error_message,
@ -120,7 +119,6 @@ Helper text mode `'on_error'` (with required)
MDTextField:
hint_text: "required = True"
text: "required = True"
required: True
helper_text_mode: "on_error"
helper_text: "Enter text"
@ -188,7 +186,7 @@ Round mode
max_text_length: 15
helper_text: "Massage"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-round-mode.gif
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-round-mode.png
:align: center
.. MDTextFieldRect:
@ -205,7 +203,6 @@ MDTextFieldRect
MDTextFieldRect:
size_hint: 1, None
height: "30dp"
background_color: app.theme_cls.bg_normal
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-rect.gif
:align: center
@ -281,17 +278,18 @@ __all__ = ("MDTextField", "MDTextFieldRect")
import os
import re
from datetime import date
from typing import Union
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.metrics import dp, sp
from kivy.properties import (
AliasProperty,
BooleanProperty,
ColorProperty,
DictProperty,
ListProperty,
NumericProperty,
ObjectProperty,
@ -313,220 +311,6 @@ with open(
Builder.load_string(kv_file.read())
# TODO: Add a class to work with the phone number mask.
class AutoFormatTelephoneNumber:
"""
Implements automatic formatting of the text entered in the text field
according to the mask, for example '+38 (###) ### ## ##'.
"""
def __init__(self):
self._backspace = False
def isnumeric(self, value):
try:
int(value)
return True
except ValueError:
return False
def do_backspace(self, *args):
if self.validator and self.validator == "phone":
self._backspace = True
text = self.text
text = text[:-1]
self.text = text
self._backspace = False
def field_filter(self, value, boolean):
if self.validator and self.validator == "phone":
if len(self.text) == 14:
return
if self.isnumeric(value):
return value
return value
def format(self, value):
if value != "" and not value.isspace() and not self._backspace:
if len(value) <= 1 and self.focus:
self.text = value
self._check_cursor()
elif len(value) == 4:
start = self.text[:-1]
end = self.text[-1]
self.text = "%s) %s" % (start, end)
self._check_cursor()
elif len(value) == 8:
self.text += "-"
self._check_cursor()
elif len(value) in [12, 16]:
start = self.text[:-1]
end = self.text[-1]
self.text = "%s-%s" % (start, end)
self._check_cursor()
def _check_cursor(self):
def set_pos_cursor(pos_corsor, interval=0.5):
self.cursor = (pos_corsor, 0)
if self.focus:
Clock.schedule_once(lambda x: set_pos_cursor(len(self.text)), 0.1)
class Validator:
"""Container class for various validation methods."""
datetime_date = ObjectProperty()
"""
The last valid date as a <class 'datetime.date'> object.
:attr:`datetime_date` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
date_interval = ListProperty([None, None])
"""
The date interval that is valid for input.
Can be entered as <class 'datetime.date'> objects or a string format.
Both values or just one value can be entered.
In string format, must follow the current date_format.
Example: Given date_format -> "mm/dd/yyyy"
Input examples -> "12/31/1900", "12/31/2100" or "12/31/1900", None.
:attr:`date_interval` is an :class:`~kivy.properties.ListProperty`
and defaults to `[None, None]`.
"""
date_format = OptionProperty(
None,
options=[
"dd/mm/yyyy",
"mm/dd/yyyy",
"yyyy/mm/dd",
],
)
"""
Format of date strings that will be entered.
Available options are: `'dd/mm/yyyy'`, `'mm/dd/yyyy'`, `'yyyy/mm/dd'`.
:attr:`date_format` is an :class:`~kivy.properties.OptionProperty`
and defaults to `None`.
"""
def is_email_valid(self, text: str) -> bool:
if not re.match(r"[^@]+@[^@]+\.[^@]+", text):
return True
return False
def is_time_valid(self, text: str) -> bool:
if re.match(r"^(2[0-3]|[01]?[0-9]):([0-5]?[0-9])$", text) or re.match(
r"^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$", text
):
return False
return True
def is_date_valid(self, text: str) -> bool:
if not self.date_format:
raise Exception("TextInput date_format was not defined.")
# Regex strings.
dd = "[0][1-9]|[1-2][0-9]|[3][0-1]"
mm = "[0][1-9]|[1][0-2]"
yyyy = "[0-9][0-9][0-9][0-9]"
fmt = self.date_format.split("/")
largs = locals()
# Access the local variables dict in the correct format based on
# date_format split. Example: "mm/dd/yyyy" -> ["mm", "dd", "yyyy"]
# largs[fmt[0]] would be largs["mm"] so the month regex string.
if re.match(
f"^({largs[fmt[0]]})/({largs[fmt[1]]})/({largs[fmt[2]]})$", text
):
input_split = text.split("/")
largs[fmt[0]] = input_split[0]
largs[fmt[1]] = input_split[1]
largs[fmt[2]] = input_split[2]
# Organize input into correct slots and try to convert
# to datetime object. This way February exceptions are
# tested. Also tests with the date_interval are simpler
# using datetime objects.
try:
datetime = date(
int(largs["yyyy"]), int(largs["mm"]), int(largs["dd"])
)
except ValueError:
return True
if self.date_interval:
if (
self.date_interval[0]
and not self.date_interval[0] <= datetime
or self.date_interval[1]
and not datetime <= self.date_interval[1]
):
return True
self.datetime_date = datetime
return False
return True
def on_date_interval(self, *args) -> None:
"""Default event handler for date_interval input."""
def on_date_interval():
if not self.date_format:
raise Exception("TextInput date_format was not defined.")
fmt = self.date_format.split("/")
largs = {}
# Convert string inputs into datetime.date objects and store
# them back into self.date_interval.
try:
if self.date_interval[0] and not isinstance(
self.date_interval[0], date
):
split = self.date_interval[0].split("/")
largs[fmt[0]] = split[0]
largs[fmt[1]] = split[1]
largs[fmt[2]] = split[2]
self.date_interval[0] = date(
int(largs["yyyy"]), int(largs["mm"]), int(largs["dd"])
)
if self.date_interval[1] and not isinstance(
self.date_interval[1], date
):
split = self.date_interval[1].split("/")
largs[fmt[0]] = split[0]
largs[fmt[1]] = split[1]
largs[fmt[2]] = split[2]
self.date_interval[1] = date(
int(largs["yyyy"]), int(largs["mm"]), int(largs["dd"])
)
except Exception:
raise Exception(
r"TextInput date_interval was defined incorrectly, it must "
r"be composed of <class 'datetime.date'> objects or strings"
r" following current date_format."
)
# Test if the interval is valid.
if isinstance(self.date_interval[0], date) and isinstance(
self.date_interval[1], date
):
if self.date_interval[0] >= self.date_interval[1]:
raise Exception(
"TextInput date_interval last date must be greater"
" than the first date or set to None."
)
Clock.schedule_once(lambda x: on_date_interval())
class MDTextFieldRect(ThemableBehavior, TextInput):
line_anim = BooleanProperty(True)
"""
@ -599,13 +383,7 @@ class TextfieldLabel(ThemableBehavior, Label):
self.font_size = sp(self.theme_cls.font_styles[self.font_style][1])
class MDTextField(
DeclarativeBehavior,
ThemableBehavior,
TextInput,
Validator,
AutoFormatTelephoneNumber,
):
class MDTextField(DeclarativeBehavior, ThemableBehavior, TextInput):
helper_text = StringProperty()
"""
Text for ``helper_text`` mode.
@ -652,185 +430,17 @@ class MDTextField(
and defaults to `'line'`.
"""
phone_mask = StringProperty("")
validator = OptionProperty(None, options=["date", "email", "time", "phone"])
"""
The type of text field for entering Email, time, etc.
Automatically sets the type of the text field as "error" if the user input
does not match any of the set validation types.
Available options are: `'date'`, `'email'`, `'time'`.
When using `'date'`, :attr:`date_format` must be defined.
.. versionadded:: 1.1.0
.. code-block:: python
MDTextField:
hint_text: "Email"
helper_text: "user@gmail.com"
validator: "email"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-validator.png
:align: center
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDBoxLayout:
orientation: "vertical"
spacing: "20dp"
adaptive_height: True
size_hint_x: .8
pos_hint: {"center_x": .5, "center_y": .5}
MDTextField:
hint_text: "Date dd/mm/yyyy without limits"
helper_text: "Enter a valid dd/mm/yyyy date"
validator: "date"
date_format: "dd/mm/yyyy"
MDTextField:
hint_text: "Date mm/dd/yyyy without limits"
helper_text: "Enter a valid mm/dd/yyyy date"
validator: "date"
date_format: "mm/dd/yyyy"
MDTextField:
hint_text: "Date yyyy/mm/dd without limits"
helper_text: "Enter a valid yyyy/mm/dd date"
validator: "date"
date_format: "yyyy/mm/dd"
MDTextField:
hint_text: "Date dd/mm/yyyy in [01/01/1900, 01/01/2100] interval"
helper_text: "Enter a valid dd/mm/yyyy date"
validator: "date"
date_format: "dd/mm/yyyy"
date_interval: "01/01/1900", "01/01/2100"
MDTextField:
hint_text: "Date dd/mm/yyyy in [01/01/1900, None] interval"
helper_text: "Enter a valid dd/mm/yyyy date"
validator: "date"
date_format: "dd/mm/yyyy"
date_interval: "01/01/1900", None
MDTextField:
hint_text: "Date dd/mm/yyyy in [None, 01/01/2100] interval"
helper_text: "Enter a valid dd/mm/yyyy date"
validator: "date"
date_format: "dd/mm/yyyy"
date_interval: None, "01/01/2100"
'''
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.screen import MDScreen
from kivymd.uix.textfield import MDTextField
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBoxLayout(
MDTextField(
hint_text="Date dd/mm/yyyy without limits",
helper_text="Enter a valid dd/mm/yyyy date",
validator="date",
date_format="dd/mm/yyyy",
),
MDTextField(
hint_text="Date mm/dd/yyyy without limits",
helper_text="Enter a valid mm/dd/yyyy date",
validator="date",
date_format="mm/dd/yyyy",
),
MDTextField(
hint_text="Date yyyy/mm/dd without limits",
helper_text="Enter a valid yyyy/mm/dd date",
validator="date",
date_format="yyyy/mm/dd",
),
MDTextField(
hint_text="Date dd/mm/yyyy in [01/01/1900, 01/01/2100] interval",
helper_text="Enter a valid dd/mm/yyyy date",
validator="date",
date_format="dd/mm/yyyy",
date_interval=["01/01/1900", "01/01/2100"],
),
MDTextField(
hint_text="Date dd/mm/yyyy in [01/01/1900, None] interval",
helper_text="Enter a valid dd/mm/yyyy date",
validator="date",
date_format="dd/mm/yyyy",
date_interval=["01/01/1900", None],
),
MDTextField(
hint_text="Date dd/mm/yyyy in [None, 01/01/2100] interval",
helper_text="Enter a valid dd/mm/yyyy date",
validator="date",
date_format="dd/mm/yyyy",
date_interval=[None, "01/01/2100"],
),
orientation="vertical",
spacing="20dp",
adaptive_height=True,
size_hint_x=0.8,
pos_hint={"center_x": 0.5, "center_y": 0.5},
)
)
)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-validator-date.png
:align: center
:attr:`validator` is an :class:`~kivy.properties.OptionProperty`
and defaults to `None`.
"""
line_color_normal = ColorProperty([0, 0, 0, 0])
"""
Line color normal (static underline line) in (r, g, b, a) or string format.
Line color normal (static underline line) in ``rgba`` format.
.. code-block:: kv
MDTextField:
hint_text: "line_color_normal"
line_color_normal: "red"
line_color_normal: 1, 0, 1, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-line-color-normal.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-line-color-normal.gif
:align: center
:attr:`line_color_normal` is an :class:`~kivy.properties.ColorProperty`
@ -839,13 +449,13 @@ class MDTextField(
line_color_focus = ColorProperty([0, 0, 0, 0])
"""
Line color focus (active underline line) in (r, g, b, a) or string format.
Line color focus (active underline line) in ``rgba`` format.
.. code-block:: kv
MDTextField:
hint_text: "line_color_focus"
line_color_focus: "red"
line_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-line-color-focus.gif
:align: center
@ -864,7 +474,7 @@ class MDTextField(
error_color = ColorProperty([0, 0, 0, 0])
"""
Error color in (r, g, b, a) or string format for ``required = True``.
Error color in ``rgba`` format for ``required = True``.
:attr:`error_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0]`.
@ -872,18 +482,7 @@ class MDTextField(
fill_color_normal = ColorProperty([0, 0, 0, 0])
"""
Fill background color in (r, g, b, a) or string format in 'fill' mode when]
text field is out of focus.
.. code=block:: kv
MDTextField:
hint_text: "Fill mode"
mode: "fill"
fill_color_normal: "brown"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-fill-color-normal.png
:align: center
Fill background color in 'fill' mode when text field is out of focus.
:attr:`fill_color_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0]`.
@ -891,18 +490,7 @@ class MDTextField(
fill_color_focus = ColorProperty([0, 0, 0, 0])
"""
Fill background color in (r, g, b, a) or string format in 'fill' mode when
the text field has focus.
.. code=block:: kv
MDTextField:
hint_text: "Fill mode"
mode: "fill"
fill_color_focus: "brown"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-fill-color-focus.gif
:align: center
Fill background color in 'fill' mode when the text field has focus.
:attr:`fill_color_focus` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0]`.
@ -926,8 +514,7 @@ class MDTextField(
hint_text_color_normal = ColorProperty([0, 0, 0, 0])
"""
Hint text color in (r, g, b, a) or string format when text field is out
of focus.
Hint text color when text field is out of focus.
.. versionadded:: 1.0.0
@ -935,9 +522,9 @@ class MDTextField(
MDTextField:
hint_text: "hint_text_color_normal"
hint_text_color_normal: "red"
hint_text_color_normal: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-hint-text-color-normal.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-hint-text-color-normal.gif
:align: center
:attr:`hint_text_color_normal` is an :class:`~kivy.properties.ColorProperty`
@ -946,8 +533,7 @@ class MDTextField(
hint_text_color_focus = ColorProperty([0, 0, 0, 0])
"""
Hint text color in (r, g, b, a) or string format when the text field has
focus.
Hint text color when the text field has focus.
.. versionadded:: 1.0.0
@ -955,7 +541,7 @@ class MDTextField(
MDTextField:
hint_text: "hint_text_color_focus"
hint_text_color_focus: "red"
hint_text_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-hint-text-color-focus.gif
:align: center
@ -966,8 +552,7 @@ class MDTextField(
helper_text_color_normal = ColorProperty([0, 0, 0, 0])
"""
Helper text color in (r, g, b, a) or string format when text field is out
of focus.
Helper text color when text field is out of focus.
.. versionadded:: 1.0.0
@ -976,7 +561,7 @@ class MDTextField(
MDTextField:
helper_text: "helper_text_color_normal"
helper_text_mode: "persistent"
helper_text_color_normal: "red"
helper_text_color_normal: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-helper-text-color-normal.png
:align: center
@ -987,8 +572,7 @@ class MDTextField(
helper_text_color_focus = ColorProperty([0, 0, 0, 0])
"""
Helper text color in (r, g, b, a) or string format when the text field has
focus.
Helper text color when the text field has focus.
.. versionadded:: 1.0.0
@ -997,7 +581,7 @@ class MDTextField(
MDTextField:
helper_text: "helper_text_color_focus"
helper_text_mode: "persistent"
helper_text_color_focus: "red"
helper_text_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-helper-text-color-focus.gif
:align: center
@ -1008,8 +592,7 @@ class MDTextField(
icon_right_color_normal = ColorProperty([0, 0, 0, 0])
"""
Color in (r, g, b, a) or string format of right icon when text field is out
of focus.
Color of right icon when text field is out of focus.
.. versionadded:: 1.0.0
@ -1018,9 +601,9 @@ class MDTextField(
MDTextField:
icon_right: "language-python"
hint_text: "icon_right_color_normal"
icon_right_color_normal: "red"
icon_right_color_normal: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-icon-right-color-normal.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-icon-right-color-normal.gif
:align: center
:attr:`icon_right_color_normal` is an :class:`~kivy.properties.ColorProperty`
@ -1029,8 +612,7 @@ class MDTextField(
icon_right_color_focus = ColorProperty([0, 0, 0, 0])
"""
Color in (r, g, b, a) or string format of right icon when the text field
has focus.
Color of right icon when the text field has focus.
.. versionadded:: 1.0.0
@ -1039,7 +621,7 @@ class MDTextField(
MDTextField:
icon_right: "language-python"
hint_text: "icon_right_color_focus"
icon_right_color_focus: "red"
icon_right_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-icon-right-color-focus.gif
:align: center
@ -1050,30 +632,47 @@ class MDTextField(
icon_left_color_normal = ColorProperty([0, 0, 0, 0])
"""
Color in (r, g, b, a) or string format of right icon when text field is out
of focus.
Color of right icon when text field is out of focus.
.. versionadded:: 1.0.0
.. code-block:: kv
MDTextField:
icon_right: "language-python"
hint_text: "icon_right_color_normal"
icon_left_color_normal: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-icon-right-color-normal.gif
:align: center
:attr:`icon_left_color_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0]`.
"""
icon_left_color_focus = ColorProperty([0, 0, 0, 0])
"""
Color in (r, g, b, a) or string format of right icon when the text field
has focus.
Color of right icon when the text field has focus.
.. versionadded:: 1.0.0
.. code-block:: kv
MDTextField:
icon_right: "language-python"
hint_text: "icon_right_color_focus"
icon_right_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-icon-right-color-focus.gif
:align: center
:attr:`icon_left_color_focus` is an :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 0]`.
"""
max_length_text_color = ColorProperty([0, 0, 0, 0])
"""
Text color in (r, g, b, a) or string format of the maximum length of
characters to be input.
Text color of the maximum length of characters to be input.
.. versionadded:: 1.0.0
@ -1081,10 +680,10 @@ class MDTextField(
MDTextField:
hint_text: "max_length_text_color"
max_length_text_color: "red"
max_length_text_color: 0, 1, 0, 1
max_text_length: 5
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-max-length-text-color.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-max-length-text-color.gif
:align: center
:attr:`max_length_text_color` is an :class:`~kivy.properties.ColorProperty`
@ -1119,7 +718,7 @@ class MDTextField(
text_color_normal = ColorProperty([0, 0, 0, 0])
"""
Text color in (r, g, b, a) or string format when text field is out of focus.
Text color in ``rgba`` format when text field is out of focus.
.. versionadded:: 1.0.0
@ -1127,9 +726,9 @@ class MDTextField(
MDTextField:
hint_text: "text_color_normal"
text_color_normal: "red"
text_color_normal: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-text-color-normal.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-text-color-normal.gif
:align: center
:attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
@ -1138,7 +737,7 @@ class MDTextField(
text_color_focus = ColorProperty([0, 0, 0, 0])
"""
Text color in (r, g, b, a) or string format when text field has focus.
Text color in ``rgba`` format when text field has focus.
.. versionadded:: 1.0.0
@ -1146,7 +745,7 @@ class MDTextField(
MDTextField:
hint_text: "text_color_focus"
text_color_focus: "red"
text_color_focus: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/text-field-text-color-focus.gif
:align: center
@ -1280,8 +879,8 @@ class MDTextField(
text=self.set_text,
)
self.theme_cls.bind(
primary_color=self.set_default_colors,
theme_style=self.set_default_colors,
primary_color=lambda x, y: self.set_default_colors(0, True),
theme_style=lambda x, y: self.set_default_colors(0, True),
)
Clock.schedule_once(self.check_text)
@ -1331,17 +930,9 @@ class MDTextField(
)
if self.error_color == [0, 0, 0, 0] or updated:
self.error_color = (
self.theme_cls.error_color
if self.error_color == [0, 0, 0, 0]
else self.error_color
)
self.error_color = self.theme_cls.error_color
if self.max_length_text_color == [0, 0, 0, 0] or updated:
self.max_length_text_color = (
self.theme_cls.disabled_hint_text_color
if self.max_length_text_color == [0, 0, 0, 0]
else self.max_length_text_color
)
self.max_length_text_color = self.theme_cls.disabled_hint_text_color
self._hint_text_color = self.hint_text_color_normal
self._text_color_normal = self.text_color_normal
@ -1510,11 +1101,8 @@ class MDTextField(
self.text = re.sub("\n", " ", text) if not self.multiline else text
self.set_max_text_length()
if self.validator and self.validator == "phone":
pass
# self.format(self.text)
if (self.text and self.max_length_text_color) or self._get_has_error():
if self.text and self.max_length_text_color and self._get_has_error():
self.error = True
if (
self.text
@ -1713,34 +1301,22 @@ class MDTextField(
if value_height >= self.max_height and self.max_height:
self.height = self.max_height
def on_text_color_normal(
self, instance_text_field, color: Union[list, str]
):
def on_text_color_normal(self, instance_text_field, color: list):
self._text_color_normal = color
def on_hint_text_color_normal(
self, instance_text_field, color: Union[list, str]
):
def on_hint_text_color_normal(self, instance_text_field, color: list):
self._hint_text_color = color
def on_helper_text_color_normal(
self, instance_text_field, color: Union[list, str]
):
def on_helper_text_color_normal(self, instance_text_field, color: list):
self._helper_text_color = color
def on_icon_right_color_normal(
self, instance_text_field, color: Union[list, str]
):
def on_icon_right_color_normal(self, instance_text_field, color: list):
self._icon_right_color = color
def on_line_color_normal(
self, instance_text_field, color: Union[list, str]
):
def on_line_color_normal(self, instance_text_field, color: list):
self._line_color_normal = color
def on_max_length_text_color(
self, instance_text_field, color: Union[list, str]
):
def on_max_length_text_color(self, instance_text_field, color: list):
self._max_length_text_color = color
def _set_color(self, attr_name: str, color: str, updated: bool) -> None:
@ -1777,13 +1353,6 @@ class MDTextField(
the :attr:`~MDTextField.required` parameter is set to `True`.
"""
if self.validator and self.validator != "phone":
has_error = {
"date": self.is_date_valid,
"email": self.is_email_valid,
"time": self.is_time_valid,
}[self.validator](self.text)
return has_error
if self.max_text_length and len(self.text) > self.max_text_length:
has_error = True
else:
@ -1798,12 +1367,9 @@ class MDTextField(
if __name__ == "__main__":
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
Window.size = (800, 750)
from kivymd.app import MDApp
KV = """
@ -1819,53 +1385,41 @@ MDScreen:
MDTextField:
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
mode: "rectangle"
max_text_length: 5
MDTextField:
icon_left: "git"
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
mode: "rectangle"
MDTextField:
icon_left: "git"
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
mode: "fill"
MDTextField:
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
mode: "fill"
MDTextField:
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
MDTextField:
icon_left: "git"
hint_text: "Label"
helper_text: "Error message"
helper_text: "Error massage"
MDTextField:
hint_text: "Round mode"
mode: "round"
max_text_length: 15
helper_text: "Message"
MDTextField:
hint_text: "Date dd/mm/yyyy in [01/01/1900, 01/01/2100] interval"
helper_text: "Enter a valid dd/mm/yyyy date"
validator: "date"
date_format: "dd/mm/yyyy"
date_interval: "01/01/1900", "01/01/2100"
MDTextField:
hint_text: "Email"
helper_text: "user@gmail.com"
validator: "email"
helper_text: "Massage"
MDFlatButton:
text: "SET TEXT"
@ -1875,8 +1429,6 @@ MDScreen:
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def set_text(self):

View File

@ -121,8 +121,8 @@ Shadow elevation control
.. code-block:: kv
MDTopAppBar:
title: "Elevation 4"
elevation: 4
title: "Elevation 10"
elevation: 10
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-7.png
:align: center
@ -327,7 +327,7 @@ Material design 3 style
:align: center
"""
__all__ = ("MDTopAppBar", "MDBottomAppBar", "ActionTopAppBarButton")
__all__ = ("MDTopAppBar", "MDBottomAppBar")
import os
from math import cos, radians, sin
@ -337,8 +337,10 @@ from kivy.animation import Animation
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.metrics import dp
from kivy.properties import (
AliasProperty,
BooleanProperty,
ColorProperty,
ListProperty,
@ -354,15 +356,15 @@ from kivymd import uix_path
from kivymd.color_definitions import text_colors
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
CommonElevationBehavior,
DeclarativeBehavior,
ScaleBehavior,
FakeRectangularElevationBehavior,
SpecificBackgroundColorBehavior,
)
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
from kivymd.uix.controllers import WindowController
from kivymd.uix.list import OneLineIconListItem
from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.templates import ScaleWidget
from kivymd.uix.tooltip import MDTooltip
from kivymd.utils.set_bars_colors import set_bars_colors
@ -372,7 +374,7 @@ with open(
Builder.load_string(kv_file.read())
class ActionBottomAppBarButton(MDFloatingActionButton, ScaleBehavior):
class ActionBottomAppBarButton(MDFloatingActionButton, ScaleWidget):
"""
Implements a floating action button (FAB) for a toolbar with type 'bottom'.
"""
@ -407,11 +409,11 @@ class OverFlowMenuItem(OneLineIconListItem):
class NotchedBox(
ThemableBehavior,
CommonElevationBehavior,
FakeRectangularElevationBehavior,
SpecificBackgroundColorBehavior,
BoxLayout,
):
elevation = NumericProperty(4)
elevation = NumericProperty(6)
notch_radius = NumericProperty()
notch_center_x = NumericProperty("100dp")
@ -959,10 +961,8 @@ class MDTopAppBar(DeclarativeBehavior, NotchedBox, WindowController):
self.icon_color = self.theme_cls.primary_color
self.bind(specific_text_color=self.update_action_bar_text_colors)
self.theme_cls.bind(
material_style=self.update_bar_height,
primary_palette=self.update_md_bg_color,
)
self.theme_cls.bind(material_style=self.update_bar_height)
self.theme_cls.bind(primary_palette=self.update_md_bg_color)
Clock.schedule_once(
lambda x: self.on_left_action_items(0, self.left_action_items)
@ -1103,7 +1103,6 @@ class MDTopAppBar(DeclarativeBehavior, NotchedBox, WindowController):
+ self.theme_cls.standard_increment / 2
+ self._shift
)
self.shadow_offset = [0, 30]
self.on_mode(None, self.mode)
def on_type_height(self, instance_toolbar, height_type_value: str) -> None:

View File

@ -314,7 +314,7 @@ class MDTooltip(ThemableBehavior, HoverBehavior, TouchBehavior):
Clock.schedule_once(self.animation_tooltip_dismiss)
def on_show(self) -> None:
"""Default display event handler."""
"""Default dismiss event handler."""
def on_dismiss(self) -> None:
"""

View File

@ -35,9 +35,7 @@ __all__ = (
"MDTransitionBase",
)
from kivy import Logger
from kivy.animation import Animation, AnimationTransition
from kivy.properties import DictProperty
from kivy.uix.screenmanager import (
ScreenManagerException,
SlideTransition,
@ -45,41 +43,13 @@ from kivy.uix.screenmanager import (
TransitionBase,
)
from kivymd.uix.hero import MDHeroFrom, MDHeroTo
from kivymd.uix.screenmanager import MDScreenManager
class MDTransitionBase(TransitionBase):
"""
TransitionBase is used to animate 2 screens within the
:class:`~kivymd.uix.screenmanager.MDScreenManager`.
For more
information, see in the :class:`~kivy.uix.screenmanager.TransitionBase`
class documentation.
"""
_direction = "in"
# Collection of child widgets of all 'MDHeroFrom' widgets that are
# on the screen, for example:
#
# MDScreen:
#
# MDHeroFrom:
# tag: "kivymd"
#
# FitImage:
#
# MDHeroFrom:
# tag: "kivy"
#
# FitImage:
#
# {
# 'kivy': <kivymd.uix.fitimage.fitimage.FitImage object>,
# 'kivymd': <kivymd.uix.fitimage.fitimage.FitImage object>,
# }
_hero_from_widget_children = DictProperty()
hero_widget = None
hero_from_widget = None # kivymd.uix.hero.MDHeroFrom object
def start(self, instance_screen_manager: MDScreenManager) -> None:
super().start(instance_screen_manager)
@ -89,160 +59,67 @@ class MDTransitionBase(TransitionBase):
]()
def animated_hero_in(self) -> None:
"""Animates the flight of heroes from screen **A** to screen **B**."""
if self.manager._heroes_data and self.manager.current_hero:
self.hero_from_widget = self.manager.get_hero_from_widget()
self._check_widget_properties()
self.hero_widget = self.hero_from_widget.children[0]
self.hero_from_widget.remove_widget(self.hero_widget)
if self.manager._heroes_data and self.manager.current_heroes:
for hero_from_widget in self.manager.get_hero_from_widget():
for heroes_tag in self.manager.current_heroes:
if heroes_tag == hero_from_widget.tag:
self._check_widget_properties(hero_from_widget)
self.hero_widget.pos = self.screen_out.to_widget(
*self.hero_from_widget.to_window(*self.hero_from_widget.pos)
)
self.hero_widget.size = self.hero_from_widget.size
self.manager.get_root_window().add_widget(self.hero_widget)
# Get child widget of the 'MDHeroFrom' container.
hero_widget = hero_from_widget.children[0]
self._hero_from_widget_children[
hero_from_widget.tag
] = hero_widget
# Removing the child widget from the 'MDHeroFrom'
# container.
hero_from_widget.remove_widget(hero_widget)
# We set the size, position of the child widget of the
# 'MDHeroFrom' container and add this widget to the
# root window.
hero_widget.pos = self.screen_out.to_widget(
*hero_from_widget.to_window(*hero_from_widget.pos)
)
hero_widget.size = hero_from_widget.size
self.manager.get_root_window().add_widget(hero_widget)
# Animating widgets added to the root window.
if self.screen_in.heroes_to:
for hero_to_widget in self.screen_in.heroes_to:
self._check_hero_to_widget_tag(
hero_to_widget, hero_from_widget
)
if hero_to_widget.tag == heroes_tag:
Animation(
size=hero_to_widget.size,
d=self.duration,
pos=hero_to_widget.pos,
).start(hero_widget)
hero_from_widget.dispatch(
"on_transform_in",
hero_widget,
self.duration,
)
Animation(
size=self.screen_in.hero_to.size,
d=self.duration,
pos=self.screen_in.hero_to.pos,
).start(self.hero_widget)
self.hero_from_widget.dispatch(
"on_transform_in", self.hero_widget, self.duration
)
def animated_hero_out(self) -> None:
"""Animates the flight of heroes from screen **B** to screen **A**."""
if self.manager._heroes_data and self.manager.current_hero:
self.screen_out.hero_to.remove_widget(self.hero_widget)
self.manager.get_root_window().add_widget(self.hero_widget)
if (
self.manager._heroes_data
and self.manager.current_heroes
and self.screen_out.heroes_to
):
for heroes_tag in self.manager.current_heroes:
for hero_to_widget in self.screen_out.heroes_to:
if hero_to_widget.tag == heroes_tag:
hero_from_children = self._hero_from_widget_children[
heroes_tag
]
hero_to_widget.remove_widget(hero_from_children)
self.manager.get_root_window().add_widget(
hero_from_children
)
for (
hero_from_widget
) in self.manager.get_hero_from_widget():
hero_from_widget.dispatch(
"on_transform_out",
self._hero_from_widget_children[
hero_from_widget.tag
],
self.duration,
)
Animation(
pos=self.screen_in.to_widget(
*hero_from_widget.to_window(
*hero_from_widget.pos
)
),
size=hero_from_widget.size,
d=self.duration,
).start(
self._hero_from_widget_children[
hero_from_widget.tag
]
)
self.hero_from_widget.dispatch(
"on_transform_out", self.hero_widget, self.duration
)
Animation(
pos=self.screen_in.to_widget(
*self.hero_from_widget.to_window(*self.hero_from_widget.pos)
),
size=self.hero_from_widget.size,
d=self.duration,
).start(self.hero_widget)
def on_complete(self) -> None:
"""
Override method.
See :attr:`kivy.uix.screenmanager.TransitionBase.on_complete'.
"""
super().on_complete()
if self.manager._heroes_data and self.manager.current_heroes:
for hero_from_widget in self.manager.get_hero_from_widget():
for heroes_tag in self.manager.current_heroes:
if heroes_tag == hero_from_widget.tag:
hero_from_children = self._hero_from_widget_children[
heroes_tag
]
self.manager.get_root_window().remove_widget(
hero_from_children
)
# Adding a child widget from the 'MDHeraFrom' container
# to the 'MDHeroTo' container.
if self._direction == "in":
for hero_to_widget in self.screen_in.heroes_to:
if hero_to_widget.tag == heroes_tag:
hero_to_widget.add_widget(
hero_from_children
)
# Restores the child widget for the 'MDHeraFrom'
# container.
elif self._direction == "out":
hero_from_widget.add_widget(hero_from_children)
if self._direction == "out":
self._direction = "in"
if self.manager._heroes_data and self.manager.current_hero:
self.manager.get_root_window().remove_widget(self.hero_widget)
self.hero_from_widget.add_widget(self.hero_widget)
else:
self._direction = "out"
if self.manager._heroes_data and self.manager.current_hero:
self.manager.get_root_window().remove_widget(self.hero_widget)
self.screen_in.hero_to.add_widget(self.hero_widget)
# Checks the attributes for the 'self.screen_in' screen.
# Called from the animated_hero_in method.
def _check_widget_properties(self, hero_from_widget: MDHeroFrom):
if not self.screen_in.heroes_to:
def _check_widget_properties(self):
if not self.screen_in.hero_to:
raise Exception(
f"The `heroes_to` attribute is not specified for screen "
f"{self.screen_in}"
f"The `hero_to` attribute is not specified for screen {self.screen_in}"
)
# The 'MDHeroFrom' widget allows you to place only one widget in
# itself.
if len(hero_from_widget.children) > 1:
if len(self.hero_from_widget.children) > 1:
raise Exception(
f"{hero_from_widget.__class__} accept only one widget"
f"{self.hero_from_widget.__class__} accept only one widget"
)
# For new API support.
def _check_hero_to_widget_tag(
self, hero_to_widget: MDHeroTo, hero_from_widget: MDHeroFrom
) -> None:
if not hero_to_widget.tag:
Logger.warning(
"KivyMD: "
f"Set the tag '{hero_from_widget.tag}' "
f"for the {hero_to_widget} widget to the same "
f"as for the {hero_from_widget} widget"
)
hero_to_widget.tag = hero_from_widget.tag
class MDSwapTransition(SwapTransition, MDTransitionBase):
pass

View File

@ -36,13 +36,11 @@ MDWidget
__all__ = ("MDWidget",)
from kivy.uix.widget import Widget
from kivymd.uix import MDAdaptiveWidget
from kivymd.uix.behaviors import DeclarativeBehavior
class MDWidget(DeclarativeBehavior, MDAdaptiveWidget, Widget):
class MDWidget(DeclarativeBehavior, MDAdaptiveWidget):
"""
See :class:`~kivy.uix.Widget` class documentation for more information.