working condition

This commit is contained in:
Yura 2024-09-15 20:57:02 +03:00
parent 417e54da96
commit 511e0b0379
517 changed files with 29187 additions and 32696 deletions

View file

@ -1,3 +1,10 @@
from .colorpicker import MDColorPicker # NOQA F401
from .datepicker import MDDatePicker # NOQA F401
from .timepicker import MDTimePicker # NOQA F401
from .datepicker import (
MDModalDatePicker,
MDDockedDatePicker,
MDModalInputDatePicker,
) # NOQA F401
from .timepicker import (
MDTimePickerDialVertical,
MDTimePickerDialHorizontal,
MDTimePickerInput,
) # NOQA F401

View file

@ -1 +0,0 @@
from .colorpicker import MDColorPicker # NOQA F401

View file

@ -1,299 +0,0 @@
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
#:import images_path kivymd.images_path
#:import colors kivymd.color_definitions.colors
#:import Window kivy.core.window.Window
<SelectAlphaChannelWidget>
orientation: "vertical"
adaptive_height: True
spacing: "12dp"
padding: 0, 0, 0, "8dp"
FitImage:
size_hint_y: None
height: "36dp"
source: f"{images_path}/alpha_layer.png"
radius: [8,]
canvas.after:
Color:
rgba:
root._rgb[:-1] + [root._opacity_value_selected_color]
RoundedRectangle:
pos: self.pos
size: self.size
radius: [8,]
MDSlider:
id: slider
size_hint_y: None
height: "12dp"
hint: False
max: 1
value: root._opacity_value_selected_color
on_value:
root._opacity_value_selected_color = self.value
if root.color_picker: \
root.color_picker._opacity_value_selected_color = self.value
<SliderItem@MDBoxLayout>
spacing: "12dp"
color_slider: "Red"
max: 255
adaptive_height: True
MDSlider:
id: slider
size_hint_y: None
height: "36dp"
color: colors[root.color_slider]["500"]
max: root.max
value: 1 if root.max == 1 else 0
on_value:
root.parent.dispatch("on_slide_value", root.parent.get_color())
MDLabel:
adaptive_size: True
-text_size: None, None
pos_hint: {"center_y": .5}
text:
str(int(slider.value)) \
if root.max != 1 \
else str(round(slider.value, 1))
<SliderTab>
orientation: "vertical"
padding: "12dp", "24dp", "12dp", 0
spacing: "24dp"
SliderItem:
id: slider_red
color_slider: "Red"
SliderItem:
id: slider_green
color_slider: "Green"
SliderItem:
id: slider_blue
color_slider: "Blue"
Widget:
SelectAlphaChannelWidget:
id: select_alpha_channel_widget
color_picker: root.color_picker
<GradientTab>
orientation: "vertical"
padding: "12dp", "12dp", "12dp", 0
spacing: "8dp"
MDBoxLayout:
id: color_selection_box
spacing: "12dp"
Widget:
id: gradient_widget
MDBoxLayout:
orientation: "vertical"
size_hint_x: None
width: "24dp"
canvas.before:
StencilPush
RoundedRectangle:
size: self.size
pos: self.pos
radius: root.color_picker.radius_color_scale
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
size: self.size
pos: self.pos
radius: root.color_picker.radius_color_scale
StencilPop
Image:
source: f"{images_path}/blue.png"
allow_stretch: True
keep_ratio: False
on_touch_down:
if self.collide_point(*args[1].pos): \
root.updated_canvas(self, args[1])
Image:
source: f"{images_path}/green.png"
allow_stretch: True
keep_ratio: False
on_touch_down:
if self.collide_point(*args[1].pos): \
root.updated_canvas(self, args[1])
Image:
source: f"{images_path}/yellow.png"
allow_stretch: True
keep_ratio: False
on_touch_down:
if self.collide_point(*args[1].pos): \
root.updated_canvas(self, args[1])
Image:
source: f"{images_path}/red.png"
allow_stretch: True
keep_ratio: False
on_touch_down:
if self.collide_point(*args[1].pos): \
root.updated_canvas(self, args[1])
Image:
source: f"{images_path}/black.png"
allow_stretch: True
keep_ratio: False
on_touch_down:
if self.collide_point(*args[1].pos): \
root.updated_canvas(self, args[1])
SelectAlphaChannelWidget:
id: select_alpha_channel_widget
color_picker: root.color_picker
<TabColorList>
rv: rv
RecycleView:
id: rv
key_viewclass: "viewclass"
key_size: "height"
RecycleBoxLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
padding: "8dp"
spacing: "8dp"
default_size_hint: 1, None
default_size: None, dp(48)
<ColorListItem>
size_hint_y: None
padding: "12dp"
md_bg_color: root.color
radius: [8,]
MDLabel:
text: root.hue_code
theme_text_color: "Custom"
text_color: root.text_color
halign: "center"
<MDColorPicker>
# These are the sums of the widths of the `TypeColorButton` buttons in the
# `type_color_button_box` box.
size_hint_min_x: dp(264)
MDBoxLayout:
orientation: "vertical"
MDBoxLayout:
id: header
orientation: "vertical"
padding: 0, "8dp", 0, 0
spacing: "8dp"
radius: root.radius[:2] + [0, 0]
size_hint_y: None
height: STANDARD_INCREMENT
md_bg_color:
app.theme_cls.primary_color \
if not root.default_color \
else root.default_color
MDLabel:
id: lbl_color_value
halign: "center"
shorten: True
bold: True
markup: True
MDBoxLayout:
id: type_color_button_box
adaptive_height: True
TypeColorButton:
text: "HEX"
group: "x"
size_hint_x: 1
on_release: root.type_color = self.text
TypeColorButton:
text: "RGB"
group: "x"
size_hint_x: 1
on_release: root.type_color = self.text
TypeColorButton:
text: "RGBA"
group: "x"
size_hint_x: 1
on_release: root.type_color = self.text
MDBottomNavigation:
id: bottom_navigation
use_text: False
on_switch_tabs: root.dispatch("on_switch_tabs", *args)
MDBottomNavigationItem:
id: bottom_navigation_gradient
name: "bottom navigation gradient"
icon: "gradient-vertical"
MDBottomNavigationItem:
id: view_headline
name: "view headline"
icon: "view-headline"
ColorListTab:
id: color_list_tabs
text_color_normal: 0, 0, 0, 1
on_tab_switch: self.generates_list_colors(*args)
color_picker: root
MDBottomNavigationItem:
id: tune
name: "tune"
icon: "tune"
SliderTab:
color_picker: root
on_slide_value:
root.dispatch("on_select_color", args[1])
MDBoxLayout:
size_hint_y: None
height: "48dp"
md_bg_color: app.theme_cls.bg_dark
radius: [0, 0] + root.radius[2:]
MDFlatButton:
text: root.text_button_ok
size_hint: 1, 1
on_release:
root.dispatch( \
"on_release", \
root.type_color, \
root._get_selected_color(root.selected_color))
MDFlatButton:
text: root.text_button_cancel
size_hint: 1, 1
on_release: root.dismiss()

View file

@ -1,657 +0,0 @@
"""
Components/ColorPicker
======================
.. versionadded:: 1.0.0
.. rubric:: Create, share, and apply color palettes to your UI, as well as measure the accessibility level of any color combination..
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-preview.png
:align: center
Usage
-----
.. code-block:: python
from typing import Union
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.pickers import MDColorPicker
KV = '''
MDScreen:
MDTopAppBar:
id: toolbar
title: "MDTopAppBar"
pos_hint: {"top": 1}
MDRaisedButton:
text: "OPEN PICKER"
pos_hint: {"center_x": .5, "center_y": .5}
md_bg_color: toolbar.md_bg_color
on_release: app.open_color_picker()
'''
class MyApp(MDApp):
def build(self):
return Builder.load_string(KV)
def open_color_picker(self):
color_picker = MDColorPicker(size_hint=(0.45, 0.85))
color_picker.open()
color_picker.bind(
on_select_color=self.on_select_color,
on_release=self.get_selected_color,
)
def update_color(self, color: list) -> None:
self.root.ids.toolbar.md_bg_color = color
def get_selected_color(
self,
instance_color_picker: MDColorPicker,
type_color: str,
selected_color: Union[list, str],
):
'''Return selected color.'''
print(f"Selected color is {selected_color}")
self.update_color(selected_color[:-1] + [1])
def on_select_color(self, instance_gradient_tab, color: list) -> None:
'''Called when a gradient image is clicked.'''
MyApp().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-usage.png
:align: center
"""
import os
import struct
from io import BytesIO
from typing import List, Union
from kivy.clock import Clock
from kivy.core.image import Image as CoreImage
from kivy.graphics import RoundedRectangle
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
ColorProperty,
ListProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
VariableListProperty,
)
from kivy.uix.behaviors import ButtonBehavior
from kivy.utils import get_color_from_hex, get_hex_from_color
from PIL import Image as PilImage
from PIL import ImageDraw
from kivymd import uix_path
from kivymd.color_definitions import colors as _colors
from kivymd.color_definitions import text_colors
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.dialog import BaseDialog
from kivymd.uix.tab import MDTabs, MDTabsBase, MDTabsLabel
__all__ = ("MDColorPicker",)
with open(
os.path.join(uix_path, "pickers", "colorpicker", "colorpicker.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class TypeColorButton(MDRaisedButton, MDToggleButton):
"""
The class implements the button to switch the color type -
'RGBA', 'HEX', 'RGB'.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.theme_text_color = "Custom"
self.text_color = (0, 0, 0, 1)
self.elevation = 0
class SelectAlphaChannelWidget(MDBoxLayout):
"""
The class implements the widget with the current color and slider to set
the value of the transparency of the selected color.
"""
# :class:`~kivymd.uix.colorpicker.MDColorPicker` class.
color_picker = ObjectProperty()
# The `RGB` value for the transparency preview widget of the selected
# color.
_rgb = ColorProperty([0, 0, 0, 0])
# The opacity value for the transparency preview widget of the selected
# color.
_opacity_value_selected_color = NumericProperty(1)
def on_color_picker(
self, instance_select_alpha_channel_widget, instance_color_picker
) -> None:
instance_color_picker.bind(_rgb=self.set_scale_rgb)
def set_scale_rgb(
self,
instance_color_picker,
color: Union[List[int], List[float]],
) -> None:
if color[0] > 1:
self._rgb = [x / 255.0 for x in color]
else:
self._rgb = color
class SliderTab(MDBoxLayout):
"""
The class has implemented `RGB` value sliders and a scale for setting the
transparency value of the selected color. This is the third tab on the
bottom navigation panel.
"""
# :class:`~kivymd.uix.colorpicker.MDColorPicker` class.
color_picker = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_slide_value")
def get_color(self) -> List[float]:
return [
self.ids.slider_red.ids.slider.value / 255,
self.ids.slider_green.ids.slider.value / 255,
self.ids.slider_blue.ids.slider.value / 255,
self.color_picker._opacity_value_selected_color,
]
def on_slide_value(self, *args) -> None:
"""Basic event handler for changing the slider value."""
class GradientTab(MDBoxLayout):
"""
The class implements a tab with a gradient, a color selection scale and
a scale for setting the transparency value of the selected color.
This is the first tab on the bottom navigation panel.
"""
# :class:`~kivymd.uix.colorpicker.MDColorPicker` class.
color_picker = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rectangle = None
self.texture = None
Clock.schedule_once(lambda x: self.create_gradient_texture())
Clock.schedule_once(self.create_canvas_with_gradient_texture)
def create_gradient_texture(
self, r_g_b=None, interval: Union[int, float] = 0
) -> None:
"""
Creates a gradient value buffer and texture object.
Called when clicking on the gradient bar to the right.
"""
# TODO: Perhaps there is a better way to create a gradient.
# The implementation using the PIL package is most likely not the most
# better. In any case, performance tests should be carried out.
gradient_widget_width = int(self.ids.gradient_widget.width)
gradient_widget_height = int(self.ids.gradient_widget.height - dp(100))
img = PilImage.new(
"RGBA", (gradient_widget_width, gradient_widget_height), "#FFFFFF"
)
draw = ImageDraw.Draw(img)
if not self.color_picker.default_color:
r, g, b = (
r_g_b
if r_g_b
else self.color_picker.get_rgb(self.theme_cls.primary_color)
)
else:
r, g, b = [
int(value * 255)
for value in self.color_picker.default_color[:-1]
]
self.color_picker._rgb = [r, g, b]
(
r_adjacent_color_constant,
g_adjacent_color_constant,
b_adjacent_color_constant,
) = (
self.color_picker.adjacent_color_constants
if r_g_b != (0, 0, 0)
else (0.40, 0.40, 0.40) # if the selected color is black
)
for i in range(gradient_widget_width):
r, g, b = (
r + r_adjacent_color_constant,
g + g_adjacent_color_constant,
b + b_adjacent_color_constant,
)
draw.line(
(i, 0, i, gradient_widget_width), fill=(int(r), int(g), int(b))
)
data = BytesIO()
img.save(data, format="png")
data.seek(0)
self.texture = CoreImage(BytesIO(data.read()), ext="png").texture
def create_canvas_with_gradient_texture(
self, interval: Union[int, float]
) -> None:
"""Creates a canvas with a gradient texture."""
with self.ids.color_selection_box.canvas:
self.rectangle = RoundedRectangle(
texture=self.texture,
pos=self.ids.gradient_widget.pos,
size=self.ids.gradient_widget.size,
radius=self.color_picker.radius,
group="gradient",
)
self.bind(
size=lambda instance, size: Clock.schedule_once(
lambda dt: self._update_canvas(instance, size)
)
)
def get_rgba_color_from_touch_region(self, widget, touch) -> List[int]:
"""
Returns the color of the pixel in the gradient that was clicked.
"""
pixel = widget.texture.get_region(*touch.pos, 1, 1)
rgba = struct.unpack("4B", pixel.pixels)
return rgba
def updated_canvas(self, widget, touch, color=None) -> None:
"""
Called when clicking on the gradient bar to the right.
Updates the color of the gradient texture.
"""
if self.color_picker.default_color:
self.color_picker.default_color = None
self.ids.color_selection_box.canvas.remove_group("gradient")
if not color:
# (0-255, 0-255, 0-255, 0-255)
color = self.get_rgba_color_from_touch_region(widget, touch)
self.create_gradient_texture(color[:-1])
self.color_picker.dispatch(
"on_select_color", [x / 255.0 for x in color]
)
else:
self.create_gradient_texture(color)
self.create_canvas_with_gradient_texture(0)
def on_touch_down(self, touch):
"""Handles the ``self.ids.gradient_widget`` touch event."""
if self.ids.gradient_widget.collide_point(*touch.pos):
color = self.get_rgba_color_from_touch_region(self, touch)
self.color_picker.dispatch(
"on_select_color", [x / 255.0 for x in color]
)
return super().on_touch_down(touch)
def _update_canvas(self, instance_gradient_widget, size: list) -> None:
self.rectangle.size = self.ids.gradient_widget.size
self.rectangle.pos = self.ids.gradient_widget.pos
class TabColorList(MDBoxLayout, MDTabsBase):
"""Implements a tab for :class:`~ColorListTab` class."""
class ColorListTab(MDTabs):
"""
The class implements a tab with tabs with a list of colors.
This is the second tab on the bottom navigation panel.
"""
# :class:`~kivymd.uix.colorpicker.MDColorPicker` class.
color_picker = ObjectProperty()
def generates_list_colors(
self,
instance_color_list_tab,
instance_tab_color_list: TabColorList,
instance_tabs_label: MDTabsLabel,
tab_label_text: str,
) -> None:
"""
Generates list of colors.
Called when you click the tab of :class:`~TabColorList` class.
"""
if not tab_label_text:
tab_label_text = "Red"
if not instance_tab_color_list.rv.data:
for hue in _colors[tab_label_text]:
color = get_color_from_hex(_colors[tab_label_text][hue])
if tab_label_text == "Light":
text_color = (0, 0, 0, 1)
elif tab_label_text == "Dark":
text_color = (1, 1, 1, 1)
else:
text_color = text_colors[tab_label_text][hue]
instance_tab_color_list.rv.data.append(
{
"viewclass": "ColorListItem",
"color": color,
"hue_code": hue,
"text_color": text_color,
"on_press": lambda x=color: self.on_press_color_item(x),
}
)
def on_press_color_item(self, color: list) -> None:
"""Called when you click on the color item from the list of colors."""
rgb = [int(value * 255) for value in color[:-1]]
self.color_picker._rgb = rgb
self.background_color = color
self.color_picker.dispatch("on_select_color", color)
class ColorListItem(RectangularRippleBehavior, ButtonBehavior, MDBoxLayout):
"""Implements the item for the list of :class:`~TabColorList` class."""
color = ColorProperty()
text_color = ColorProperty()
hue_code = StringProperty()
class MDColorPicker(BaseDialog):
adjacent_color_constants = ListProperty([0.299, 0.887, 0.411])
"""
A list of values that are used to create the gradient. These values are
selected empirically. Each of these values will be added to the selected
``RGB`` value, thus creating colors that are close in value.
:attr:`adjacent_color_constants` is an :class:`~kivy.properties.ListProperty`
and defaults to `[0.299, 0.887, 0.411]`.
"""
default_color = ColorProperty(None, allownone=True)
"""
Default color value in (r, g, b, a) or string format. The set color value
will be used when you open the dialog.
:attr:`default_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
type_color = OptionProperty("RGB", options=["RGBA", "HEX", "RGB"])
"""
Type of color.
Available options are: `'RGBA'`, `'HEX'`, `'RGB'`.
:attr:`type_color` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'RGB'`.
"""
background_down_button_selected_type_color = ColorProperty([1, 1, 1, 0.3])
"""
Button background for choosing a color type ('RGBA', 'HEX', 'HSL', 'RGB')
in (r, g, b, a) or string format.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-background-down-button-selected-type-color.png
:align: center
:attr:`background_down_button_selected_type_color` is an
:class:`~kivy.properties.ColorProperty` and defaults to `[1, 1, 1, 0.3]`.
"""
radius_color_scale = VariableListProperty([8])
"""
The radius value for the color scale.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-gradient-scale-radius.png
:align: center
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[8, 8, 8, 8]`.
"""
text_button_ok = StringProperty("SELECT")
"""
Color selection button text.
:attr:`text_button_ok` is an :class:`~kivy.properties.StringProperty`
and defaults to `'SELECT'`.
"""
text_button_cancel = StringProperty("CANCEL")
"""
Cancel button text.
:attr:`text_button_cancel` is an :class:`~kivy.properties.StringProperty`
and defaults to `'CANCEL'`.
"""
selected_color = None
# One of the objects of classes:
# :class:`~GradientTab`, :class:`~ColorListTab`, :class:`~SliderTab`.
_current_tab = ObjectProperty()
# The `RGB` value for the transparency preview widget of the selected
# color.
_rgb = ListProperty()
# The opacity value for the transparency preview widget of the selected
# color.
_opacity_value_selected_color = NumericProperty(1)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.gradient_tab = None
self.register_event_type("on_select_color")
self.register_event_type("on_switch_tabs")
self.register_event_type("on_release")
self.on_background_down_button_selected_type_color(
None, self.background_down_button_selected_type_color
)
self.on_background_down_button_selected_type_color(
None, self.background_down_button_selected_type_color
)
Clock.schedule_once(lambda x: self.on_type_color(self), 1)
def update_color_slider_item_bottom_navigation(self, color: list) -> None:
"""
Updates the color of the slider that sets the transparency value of the
selected color and the color of bottom navigation items.
"""
if "select_alpha_channel_widget" in self._current_tab.ids:
self._current_tab.ids.select_alpha_channel_widget.ids.slider.color = (
color
)
self.ids.bottom_navigation.text_color_active = color
def update_color_type_buttons(self, color: list) -> None:
"""
Updating button colors (display buttons of type of color) to match the
selected color.
"""
for instance_toggle_button in self.ids.type_color_button_box.children:
if instance_toggle_button.state != "down":
instance_toggle_button.md_bg_color = color
instance_toggle_button.background_normal = color
def get_rgb(self, color: list) -> list:
"""Returns an ``RGB`` list of values from 0 to 255."""
return [
int(value * 255)
for value in (color[:-1] if len(color) == 4 else color)
]
def on_background_down_button_selected_type_color(
self, instance_color_picker, color: list
) -> None:
def set_background_down(interval: Union[float, int]) -> None:
for (
instance_toggle_button
) in self.ids.type_color_button_box.children:
instance_toggle_button.background_down = color
if self.type_color == instance_toggle_button.text:
instance_toggle_button.state = "down"
Clock.schedule_once(set_background_down)
def on_type_color(
self,
instance_color_picker,
type_color: str = "",
interval: Union[float, int] = 0,
) -> None:
"""Called when buttons are clicked to set the color type."""
if not type_color:
type_color = self.type_color
if self._rgb:
rgb = self._rgb if self._rgb[0] > 1 else self.get_rgb(self._rgb)
opacity = self._opacity_value_selected_color
color = ""
if type_color == "RGB":
self.selected_color = [value for value in rgb]
color = f"RGB({', '.join([str(value) for value in self.selected_color])})"
elif type_color == "RGBA":
self.selected_color = [x / 255.0 for x in rgb] + [opacity]
color = f"RGBA({', '.join([str(x / 255.0) for x in rgb])}, {opacity})"
elif type_color == "HEX":
self.selected_color = get_hex_from_color(
[x / 255.0 for x in rgb] + [opacity]
)
color = f"HEX({self.selected_color})"
self.ids.lbl_color_value.text = color
def on_open(self) -> None:
"""Default open event handler."""
if not self.ids.bottom_navigation_gradient.children:
self.gradient_tab = GradientTab(color_picker=self)
self._current_tab = self.gradient_tab
self.ids.bottom_navigation_gradient.add_widget(self.gradient_tab)
super().on_open()
def on_select_color(self, color: list) -> None:
"""Called when a gradient image is clicked."""
if len(color) == 3:
color += [self._opacity_value_selected_color]
self.ids.header.md_bg_color = color
self._rgb = color[:-1]
self.on_type_color(self, self.type_color)
self.update_color_type_buttons(color)
self.update_color_slider_item_bottom_navigation(color)
def on_switch_tabs(
self,
bottom_navigation_instance,
bottom_navigation_item_instance,
name_tab,
) -> None:
"""Called when switching tabs of bottom navigation."""
if name_tab == "bottom navigation gradient":
self._current_tab = self.gradient_tab
bottom_navigation_item_instance.children[0].updated_canvas(
None,
None,
self._rgb if self._rgb[0] > 1 else self.get_rgb(self._rgb),
)
instance_slider_tab = (
bottom_navigation_instance.ids.tab_manager.get_screen(
"tune"
).children[0]
)
select_alpha_channel_widget = (
self.gradient_tab.ids.select_alpha_channel_widget
)
select_alpha_channel_widget.ids.slider.value = (
instance_slider_tab.ids.select_alpha_channel_widget.ids.slider.value
)
select_alpha_channel_widget.ids.slider.color = [
x / 255.0 for x in self._rgb
] + [1]
elif name_tab == "tune":
if self._rgb[0] <= 1:
color = self.get_rgb(self._rgb)
else:
color = self._rgb
instance_slider_tab = self.ids.tune.children[0]
self._current_tab = instance_slider_tab
instance_slider_tab.ids.slider_red.ids.slider.value = color[0]
instance_slider_tab.ids.slider_green.ids.slider.value = color[1]
instance_slider_tab.ids.slider_blue.ids.slider.value = color[2]
instance_slider_tab.ids.select_alpha_channel_widget.ids.slider.value = (
self._opacity_value_selected_color
)
elif name_tab == "view headline":
color = self._rgb + [1]
color_list_tabs = self.ids.view_headline.children[0]
self._current_tab = color_list_tabs
try:
color_list_tabs.background_color = color
except ValueError:
color_list_tabs.background_color = [x / 255.0 for x in color][
:-1
] + [1]
if not color_list_tabs.get_tab_list():
for color in _colors.keys():
tab_widget = TabColorList(title=str(color))
color_list_tabs.add_widget(tab_widget)
def on_release(self, *args):
"""Called when the `SELECT` button is pressed"""
def _get_selected_color(self, selected_color: Union[list, str]) -> list:
"""
Convert [0-255, 0-255, 0-255] and '#rrggbb' to kivy color format.
Return kivy color format.
"""
rgba = [0, 0, 0, 0]
if isinstance(selected_color, list):
if selected_color[0] > 1:
rgba = [x / 255.0 for x in selected_color] + [
self._opacity_value_selected_color
]
else:
rgba = selected_color
elif isinstance(selected_color, str):
rgba = get_color_from_hex(selected_color)[:-1] + [
self._opacity_value_selected_color
]
return rgba

View file

@ -1,5 +1,5 @@
from .datepicker import ( # NOQA F401
BaseDialogPicker,
DatePickerInputField,
MDDatePicker,
MDDockedDatePicker,
MDModalDatePicker,
MDModalInputDatePicker,
)

View file

@ -1,392 +1,448 @@
#:import os os
#:import date datetime.date
#:import calendar calendar
#:import platform platform
#:import Clock kivy.clock.Clock
#:import images_path kivymd.images_path
###############################################################################
#
# MODAL INPUT RULES
#
###############################################################################
<MDModalInputDatePickerInputField>
mode: "outlined"
validator: "date"
<DatePickerBaseTooltip>
on_enter:
self.tooltip_text = "" if self.owner \
and self.owner._input_date_dialog_open \
or self.owner._select_year_dialog_open \
else self.hint_text
<DatePickerIconTooltipButton>
<MDDatePicker>
_calendar_layout: _calendar_layout
<MDModalInputDatePicker>
orientation: "vertical"
padding: 0, dp(16), 0, dp(16)
size_hint: None, None
size:
(dp(328), dp(512) - root._shift_dialog_height) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(528), dp(328) - root._shift_dialog_height)
size: dp(328), dp(280)
pos_hint: {"center_x": .5, "center_y": .5}
MDRelativeLayout:
id: container
background: os.path.join(images_path, "transparent.png")
MDLabel:
id: supporting_label
adaptive_size: True
text: root.supporting_text
padding: dp(16), 0, 0, dp(36)
font_style: "Label"
role: "large"
canvas:
Color:
rgb: root.primary_color or app.theme_cls.primary_color
RoundedRectangle:
size:
(dp(328), dp(120)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(168), dp(328) - root._shift_dialog_height)
pos:
(0, root.height - dp(120)) \
if root.theme_cls.device_orientation == "portrait" \
else (0, 0)
radius:
(root.radius[0], root.radius[1], dp(0), dp(0)) \
if root.theme_cls.device_orientation == "portrait" \
else (root.radius[0], dp(0), dp(0), root.radius[3])
Color:
rgba: root.accent_color or app.theme_cls.bg_normal
RoundedRectangle:
size:
(dp(328), dp(512) - dp(120) - root._shift_dialog_height) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(360), dp(328) - root._shift_dialog_height)
pos:
(0, 0) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(168), 0)
radius:
(dp(0), dp(0), root.radius[2], root.radius[3]) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(0), root.radius[1], root.radius[2], dp(0))
BoxLayout:
size_hint_y: None
height: self.minimum_height
padding: dp(16), 0, dp(8), dp(16)
MDLabel:
id: label_title
font_style: "Body2"
bold: True
theme_text_color: "Custom"
size_hint_x: None
width: root.width
adaptive_height: True
text: root.title
font_name: root.font_name
pos:
(dp(24), root.height - self.height - dp(18)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(24), root.height - self.height - dp(24))
text_color: root.text_toolbar_color or root.specific_text_color
id: current_month_name
adaptive_size: True
text: root._current_month_name
text_color: self.theme_cls.onSurfaceVariantColor
font_style: "Headline"
role: "large"
theme_line_height: "Custom"
line_height: 1
theme_font_size: "Custom"
font_size: "32sp" if root.mode == "picker" else "20sp"
pos_hint: {"center_y": .5}
MDLabel:
id: label_full_date
font_style: "H4"
theme_text_color: "Custom"
size_hint_x: None
width: root.width
adaptive_height: True
font_name: root.font_name
markup: True
pos:
(dp(24), root.height - dp(120) + dp(18)) \
if root.theme_cls.device_orientation == "portrait" \
else \
( \
dp(24) if not root._input_date_dialog_open else dp(168) + dp(24), \
root.height - self.height - dp(96) \
)
text: root._date_label_text
text_color:
root.text_toolbar_color or root.specific_text_color \
if root.theme_cls.device_orientation == "portrait" else \
root.primary_color or self.theme_cls.primary_color \
if root._input_date_dialog_open else \
root.text_toolbar_color or root.specific_text_color
RecycleView:
id: _year_layout
key_viewclass: "viewclass"
size_hint: None, None
size: _calendar_layout.size
pos: _calendar_layout.pos
disabled: True
canvas.before:
PushMatrix
Scale:
x: root._scale_year_layout
y: root._scale_year_layout
origin: self.center
canvas.after:
PopMatrix
SelectYearList:
cols: 3
default_size: dp(170), dp(36)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
Widget:
MDIconButton:
id: edit_icon
icon: "pencil"
icon_size: "24sp"
theme_icon_color: "Custom"
on_release:
root.transformation_to_dialog_input_date() \
if not root._input_date_dialog_open else \
Clock.schedule_once(root.transformation_from_dialog_input_date, .15)
x:
(root.width - self.width - dp(12)) \
if root.theme_cls.device_orientation == "portrait" \
else dp(12)
y:
(root.height - dp(120) + dp(12)) \
if root.theme_cls.device_orientation == "portrait" \
else dp(12)
text_color: root.text_toolbar_color or root.specific_text_color
id: icon_button
icon: "calendar"
on_release: root.dispatch("on_edit")
MDDivider:
MDModalInputDatePickerInputDateFieldContainer
id: input_date_container
padding: dp(16), 0, dp(16), dp(24)
MDModalInputDatePickerInputField:
id: input_date_field
date_picker: root
date_format: root.date_format
MDTextFieldHintText:
text: root.date_format
MDTextFieldHelperText:
text: root.error_text
mode: "on_error"
MDDatePickerButtonsContainer:
date_picker: root
###############################################################################
#
# MODAL RULES
#
###############################################################################
<MDModalDatePickerYearSelectableItem>
size_hint_x: None
valign: "middle"
halign: "center"
text_color:
self.theme_cls.onSurfaceColor \
if not root.selected else \
self.theme_cls.onPrimaryColor
canvas.before:
Color:
rgba:
self.theme_cls.primaryColor \
if self.selected else \
self.theme_cls.transparentColor
RoundedRectangle:
pos: self.x + dp(12), self.y + dp(6)
size: self.width - dp(24), self.height - dp(8)
radius: [(root.height / 2) - dp(4), ]
<MDModalDatePickerMenuYearSelection>
scale_value_x: 1.5
scale_value_y: 1.5
key_viewclass: "viewclass"
key_size: "height"
bar_width: 0
MDModalDatePickerContainerMenuYearSelection:
cols: 3
padding: dp(32), 0, 0, 0
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: False
touch_multiselect: True
<MDModalDatePickerScrim>
canvas:
Color:
rgba: self.color[:-1] + [self.alpha]
Rectangle:
pos: self.pos
size: self.size
<MDModalDatePickerYearSelectionItem>
size_hint_y: None
height: dp(48)
MDLabel:
id: label
adaptive_size: True
text: root.text
pos_hint: {"center_y": .5}
padding: 0, 0, "8dp", 0
font_style: "Label"
role: "large"
MDDatePickerBaseMenuSelectionButton
id: menu_selection_button
pos_hint: {"center_y": .5}
icon: "menu-right"
date_picker: root.date_picker
on_release: root.dispatch("on_open_menu")
Widget:
MDIconButton:
id: chevron_left
icon: "chevron-left"
pos_hint: {"center_y": .5}
on_release: root.dispatch("on_release", "prev")
MDIconButton:
id: chevron_right
icon: "chevron-right"
pos_hint: {"center_y": .5}
on_release: root.dispatch("on_release", "next")
<MDModalDatePicker>
calendar_layout: calendar_layout
orientation: "vertical"
padding: 0, 0, 0, "12dp"
pos_hint: {'center_x': .5, 'center_y': .5}
size_hint: None, None
size: calendar_layout.width - self.padding[0] / 2, dp(520)
MDLabel:
id: supporting_label
adaptive_size: True
text: root.supporting_text
padding: dp(24), 0, 0, dp(36)
font_style: "Label"
role: "large"
BoxLayout:
size_hint_y: None
height: self.minimum_height
padding: dp(24), 0, dp(12), dp(16)
MDLabel:
id: label_month_selector
font_style: "Body2"
-text_size: None, None
theme_text_color: "Custom"
id: current_month_name
adaptive_size: True
text: calendar.month_name[root.month].capitalize() + " " + str(root.year)
font_name: root.font_name
pos:
(dp(24), root.height - dp(120) - self.height - dp(20)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(168) + dp(24), label_title.y)
text_color: root.text_color or app.theme_cls.text_color
text: root._current_month_name
text_color: self.theme_cls.onSurfaceVariantColor
font_style: "Headline"
role: "large"
DatePickerIconTooltipButton:
id: triangle
owner: root
icon: "menu-down"
ripple_scale: .5
theme_icon_color: "Custom"
hint_text: "Choose year"
on_release:
root.transformation_to_dialog_select_year() \
if not root._select_year_dialog_open else \
root.transformation_from_dialog_select_year()
pos:
(label_month_selector.width + dp(14), root.height - dp(123) - self.height) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(180) + label_month_selector.width, label_title.y - dp(14))
text_color: root.text_color or app.theme_cls.text_color
md_bg_color_disabled: 0, 0, 0, 0
Widget:
DatePickerIconTooltipButton:
id: chevron_left
owner: root
icon: "chevron-left"
on_release: root.change_month("prev")
theme_icon_color: "Custom"
hint_text: "Previous month"
x:
dp(228) if root.theme_cls.device_orientation == "portrait" \
else dp(418)
y:
root.height - dp(120) - self.height / 2 - dp(30) \
if root.theme_cls.device_orientation == "portrait" \
else dp(272)
text_color: root.text_color or app.theme_cls.text_color
MDIconButton:
id: icon_button
icon: "pencil"
on_release: root.dispatch("on_edit")
DatePickerIconTooltipButton:
id: chevron_right
owner: root
icon: "chevron-right"
on_release: root.change_month("next")
theme_icon_color: "Custom"
hint_text: "Next month"
x:
dp(272) if root.theme_cls.device_orientation == "portrait" \
else dp(464)
y:
root.height - dp(120) - self.height / 2 - dp(30) \
if root.theme_cls.device_orientation == "portrait" \
else dp(272)
text_color: root.text_color or app.theme_cls.text_color
MDDivider:
# TODO: Replace the GridLayout with a RecycleView
# if it improves performance.
GridLayout:
id: _calendar_layout
cols: 7
MDModalDatePickerYearSelectionItem:
id: year_selection_items
text: root._current_full_month_name
date_picker: root
padding: dp(24), 0, dp(12), 0
RelativeLayout:
size_hint: None, None
size: calendar_layout.size
MDCalendarLayout:
id: calendar_layout
padding: dp(12), 0, dp(12), 0
MDModalDatePickerMenuYearSelection
id: year_selection_layout
size_hint: None, None
size:
(dp(44 * 7), dp(40 * 7)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(46 * 7), dp(32 * 7))
col_default_width:
dp(42) if root.theme_cls.device_orientation == "portrait" \
else dp(39)
padding:
(dp(2), 0) if root.theme_cls.device_orientation == "portrait" \
else (dp(7), 0)
spacing:
(dp(2), 0) if root.theme_cls.device_orientation == "portrait" \
else (dp(7), 0)
pos:
(dp(10), dp(56)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(168) + dp(20), dp(44))
size: 0, 0
canvas.before:
PushMatrix
Scale:
x: root._scale_calendar_layout
y: root._scale_calendar_layout
origin: self.center
canvas.after:
PopMatrix
MDDatePickerButtonsContainer:
date_picker: root
MDFlatButton:
id: ok_button
width: dp(32)
pos: root.width - self.width, dp(10)
text: "OK"
theme_text_color: "Custom"
font_name: root.font_name
text_color: root.text_button_color or root.theme_cls.primary_color
on_release: root.on_ok_button_pressed()
###############################################################################
#
# DOCKED RULES
#
###############################################################################
MDFlatButton:
id: cancel_button
text: "CANCEL"
on_release: root.dispatch("on_cancel", None)
theme_text_color: "Custom"
pos: root.width - self.width - ok_button.width - dp(10), dp(10)
font_name: root.font_name
text_color: root.text_button_color or root.theme_cls.primary_color
<DatePickerDaySelectableItem>
<MDDockedDatePicker>
calendar_layout: calendar_layout
size_hint: None, None
size:
(dp(42), dp(42)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(32), dp(32))
disabled: True
size: calendar_layout.width - self.padding[0] / 2, dp(406)
orientation: "vertical"
padding: "12dp", 0, "12dp", "12dp"
BoxLayout:
id: month_year_selection_items_container
size_hint_y: None
height: self.minimum_height
padding: 0, 0, 0, "12dp"
MDDockedDatePickerMonthSelectionItem:
id: month_selection_items
text: root._current_month_name
date_picker: root
Widget:
MDDockedDatePickerYearSelectionItem:
id: year_selection_items
text: str(root.year)
date_picker: root
RelativeLayout:
size_hint: None, None
size: calendar_layout.size
MDCalendarLayout:
id: calendar_layout
MDDockedDatePickerMenuMonthYearSelection
id: month_year_selection_layout
size_hint: None, None
size: 0, 0
MDDatePickerButtonsContainer:
id: button_container
date_picker: root
<MDDockedDatePickerMenuMonthYearSelection>
scale_value_x: 0
scale_value_y: 0
key_viewclass: "viewclass"
key_size: "height"
bar_width: 0
MDDockedDatePickerContainerMenuMonthYearSelection:
padding: dp(8), 0, 0, 0
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
multiselect: False
touch_multiselect: True
<MDDockedDatePickerMenuSelectionItem>
spacing: "8dp"
size_hint_y: None
MDIcon:
icon: "check"
pos_hint: {"center_y": .5}
icon_color:
self.theme_cls.transparentColor \
if not root.selected else \
self.theme_cls.onSurfaceColor
MDLabel:
text: root.month_year_name
<MDDockedDatePickerBaseSelectionContainer>
size_hint: None, None
size: self.minimum_size
MDIconButton:
id: chevron_left
icon: "chevron-left"
on_release: root.dispatch("on_release", "prev")
MDLabel:
id: label
adaptive_size: True
text: root.text
pos_hint: {"center_y": .5}
padding: 0, 0, "8dp", 0
font_style: "Label"
role: "large"
MDDatePickerBaseMenuSelectionButton
id: menu_selection_button
pos_hint: {"center_y": .5}
icon: "menu-right"
date_picker: root.date_picker
on_release: root.dispatch("on_open_menu")
MDIconButton:
id: chevron_right
icon: "chevron-right"
on_release: root.dispatch("on_release", "next")
###############################################################################
#
# COMMON RULES
#
###############################################################################
<MDBaseDatePicker>
elevation_level: 0
shadow_color: self.theme_cls.transparentColor
canvas.before:
Color:
rgba:
self.theme_cls.surfaceContainerHighColor
RoundedRectangle:
size: self.size
pos: self.pos
radius: self.radius
<MDCalendarLayout>
cols: 7
size_hint: None, None
width: dp(46 * 7)
height: self.minimum_height
<MDDatePickerButtonsContainer>
size_hint_y: None
height: self.minimum_height
padding: 0, 0, dp(12), 0
Widget:
MDButton:
style: "text"
on_release: root.date_picker.dispatch("on_cancel")
MDButtonText:
text:
root.date_picker.text_button_cancel \
if root.date_picker else \
""
MDButton:
style: "text"
on_release: root.date_picker.dispatch("on_ok")
MDButtonText:
text:
root.date_picker.text_button_ok \
if root.date_picker else \
""
<MDDatePickerMenuButton>
icon: "menu-right"
<MDDatePickerWeekdayLabel>
size_hint: None, None
text_size: self.size
halign: "center"
valign: "middle"
size: dp(40), dp(40)
<MDDatePickerDaySelectableItem>
size_hint: None, None
size: dp(42), dp(42)
halign: "center"
radius: self.height / 2
line_color:
self.theme_cls.primaryColor \
if self.is_today and self.date_picker.mark_today else \
self.theme_cls.transparentColor
text_color:
self.theme_cls.onSurfaceColor \
if not root.is_selected else \
self.theme_cls.onPrimaryColor
# Fill marking the available dates of the range, if using the `range` mode
# or use `min_date/max_date`.
canvas.before:
Color:
rgba:
(self.owner.selector_color or self.theme_cls.primary_color)[:-1] + [.3] \
self.theme_cls.primaryColor[:-1] + [.3] \
if self.is_in_range \
else (0, 0, 0, 0)
else \
self.theme_cls.transparentColor
RoundedRectangle:
size:
(dp(44), dp(32)) \
if root.theme_cls.device_orientation == "portrait" \
else \
(dp(32), dp(28)) \
(dp(42), dp(32)) \
if self.is_range_end or self.is_week_end or self.is_month_end \
else (dp(46), dp(28))
pos:
(self.x - dp(1.5), self.y + dp(5)) \
if root.theme_cls.device_orientation == "portrait" else \
(self.x, self.y + 1)
else (dp(42), dp(32))
pos: self.x, self.y + dp(6)
radius:
[
self.width / 2 if self.is_range_start else 0,
self.width / 2 if self.is_range_end else 0,
self.width / 2 if self.is_range_end else 0,
self.width / 2 if self.is_range_start else 0,
dp(32) / 2 if self.is_range_start else 0,
dp(32) / 2 if self.is_range_end else 0,
dp(32) / 2 if self.is_range_end else 0,
dp(32) / 2 if self.is_range_start else 0,
]
# Selection circle.
Color:
rgba:
root.owner.selector_color or self.theme_cls.primary_color \
self.theme_cls.primaryColor \
if root.is_selected and not self.disabled \
else (0, 0, 0, 0)
else self.theme_cls.transparentColor
Ellipse:
size:
(dp(42), dp(42)) \
if root.theme_cls.device_orientation == "portrait" \
else (dp(32), dp(32))
size: dp(42), dp(42)
pos: self.pos
MDLabel:
font_style: "Caption"
size_hint_x: None
halign: "center"
text: root.text
font_name: root.owner.font_name
theme_text_color: "Custom"
text_color:
root.owner.accent_color or root.theme_cls.bg_normal \
if root.is_selected else \
root.owner.text_current_color or root.theme_cls.primary_color \
if root.is_today else \
root.owner.text_color or root.theme_cls.text_color
<DatePickerWeekdayLabel>
font_style: "Caption"
theme_text_color: "Custom"
size_hint: None, None
text_size: self.size
halign: "center"
valign:
"middle" if root.theme_cls.device_orientation == "portrait" \
else "center"
size:
(dp(40), dp(40)) if root.theme_cls.device_orientation == "portrait" \
else (dp(32), dp(32))
text_color: root.owner.text_weekday_color or app.theme_cls.disabled_hint_text_color
<DatePickerYearSelectableItem>
font_style: "Caption"
size_hint_x: None
valign: "middle"
halign: "center"
text: root.text
theme_text_color: "Custom"
text_color:
(0, 0, 0, 0) \
if self.owner is None else \
self.owner.accent_color or self.owner.theme_cls.bg_normal \
if self.selected else \
self.owner.text_color or self.owner.theme_cls.text_color
on_text: root.font_name = root.owner.font_name
canvas.before:
Color:
rgba:
self.owner.selector_color or self.theme_cls.primary_color \
if self.selected else \
(0, 0, 0, 0)
RoundedRectangle:
pos: self.x + dp(12), self.y
size: self.width - dp(24), self.height
radius: [root.height / 2, ]
<DatePickerInputFieldContainer>
adaptive_height: True
size_hint_x: None
spacing: dp(8)
opacity: 0
width:
self.owner.width - dp(48) \
if root.owner.theme_cls.device_orientation == "portrait" \
else self.owner.width - dp(168) - dp(48)
y:
self.owner.height - dp(123) - self.height - dp(20) \
if root.owner.theme_cls.device_orientation == "portrait" \
else self.owner.height - self.height - dp(24)
x:
dp(24) if root.owner.theme_cls.device_orientation == "portrait" \
else dp(168) + dp(24)
<DatePickerInputField>
mode: "fill"
hint_text: "dd/mm/yyyy"
input_filter: root.input_filter
fill_color: root.owner.input_field_background_color or (0, 0, 0, .15)

View file

@ -1 +1,5 @@
from .timepicker import MDTimePicker # NOQA F401
from .timepicker import (
MDTimePickerDialVertical,
MDTimePickerDialHorizontal,
MDTimePickerInput,
) # NOQA F401

View file

@ -1,132 +1,275 @@
<TimeInputLabel@MDLabel>:
theme_text_color: "Custom"
font_size: dp(10)
halign: "left"
valign: "bottom"
adaptive_size: True
###############################################################################
#
# INPUT RULES
#
###############################################################################
<AmPmSelectorLabel>
halign: "center"
valign: "center"
theme_text_color: "Custom"
<AmPmSelector>
<MDTimePickerInput>
size_hint: None, None
size: dp(328), dp(248)
_time_input: _time_input
# _selector: _selector
_am_pm_selector: _am_pm_selector
MDLabel:
adaptive_size: True
text: root.headline_text
font_style: "Label"
role: "large"
padding: 0, 0, 0, dp(20)
BoxLayout:
size_hint_y: None
height: self.minimum_height
spacing: dp(20)
BoxLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
MDTimePickerInputContainer:
id: _time_input
time_picker: root
_readonly: False
# root._get_time_input(*self.get_time())
BoxLayout:
size_hint_y: None
height: self.minimum_height
padding: 0, dp(8), 0, 0
MDLabel:
role: "small"
adaptive_height: True
text: "Hour"
MDLabel:
role: "small"
adaptive_height: True
text: "Minute"
padding_x: dp(20)
BoxLayout:
size_hint: None, None
size: self.minimum_size
padding: 0, 0, 0, dp(26)
MDTimePickerAmPmSelector:
id: _am_pm_selector
size_hint: None, None
size: dp(52), dp(80)
on_selected:
root.dispatch("on_am_pm", self.selected)
root._get_am_pm(self.selected)
MDTimePickerButtonsContainer:
time_picker: root
icon: "clock-outline"
###############################################################################
#
# DIAL RULES
#
###############################################################################
<MDTimePickerDialHorizontal>
size_hint: None, None
size: dp(574), dp(380)
_time_input: _time_input
_selector: _selector
_am_pm_selector: _am_pm_selector
MDLabel:
adaptive_size: True
text: root.headline_text
font_style: "Label"
role: "large"
padding: 0, 0, 0, dp(20)
BoxLayout:
spacing: dp(52)
BoxLayout:
orientation: "vertical"
spacing: dp(16)
padding: 0, 0, 0, dp(52)
MDTimePickerInputContainer:
id: _time_input
time_picker: root
on_minute_select: _selector.switch_mode("minute")
on_hour_select: _selector.switch_mode("hour")
MDTimePickerAmPmSelector:
id: _am_pm_selector
orientation: "horizontal"
size_hint: None, None
size: _time_input.width, dp(38)
on_selected:
root.dispatch("on_am_pm", self.selected)
root._get_am_pm(self.selected)
MDTimePickerCircularSelector:
id: _selector
time_picker: root
MDTimePickerButtonsContainer:
time_picker: root
<MDTimePickerDialVertical>
size_hint: None, None
size: dp(324), dp(520)
_time_input: _time_input
_selector: _selector
_am_pm_selector: _am_pm_selector
MDLabel:
adaptive_size: True
text: root.headline_text
font_style: "Label"
role: "large"
padding: 0, 0, 0, dp(20)
BoxLayout:
size_hint_y: None
height: self.minimum_height
spacing: dp(20)
MDTimePickerInputContainer:
id: _time_input
time_picker: root
on_minute_select: _selector.switch_mode("minute")
on_hour_select: _selector.switch_mode("hour")
MDTimePickerAmPmSelector:
id: _am_pm_selector
size_hint: None, None
size: dp(52), dp(80)
on_selected:
root.dispatch("on_am_pm", self.selected)
root._get_am_pm(self.selected)
Widget:
size_hint_x: None
width: dp(20)
MDTimePickerCircularSelector:
id: _selector
time_picker: root
MDTimePickerButtonsContainer:
time_picker: root
###############################################################################
#
# COMMON RULES
#
###############################################################################
<MDBaseTimePicker>
canvas.before:
Color:
rgba: root.border_color
rgba:
self.theme_cls.surfaceContainerHighColor
RoundedRectangle:
size: self.size
pos: self.pos
radius: self.radius
orientation: "vertical"
elevation_level: 0
shadow_color: self.theme_cls.transparentColor
pos_hint: {'center_x': .5, 'center_y': .5}
padding: dp(24)
opacity: 0
<MDTimePickerButtonsContainer>
size_hint_y: None
height: self.minimum_height
padding: 0, dp(24), 0, 0
MDIconButton:
icon: root.icon
on_release: root.time_picker.dispatch("on_edit")
Widget:
MDButton:
style: "text"
on_release: root.time_picker.dispatch("on_cancel")
MDButtonText:
text:
root.time_picker.text_button_cancel \
if root.time_picker else \
""
MDButton:
style: "text"
on_release: root.time_picker.dispatch("on_ok")
MDButtonText:
text:
root.time_picker.text_button_ok \
if root.time_picker else \
""
<MDTimePickerScrim>
canvas:
Color:
rgba: self.color[:-1] + [self.alpha]
Rectangle:
pos: self.pos
size: self.size
radius: [root.border_radius, ]
#AM
<MDTimePickerInputTextField>
canvas.after:
Color:
rgba: root._am_bg_color
RoundedRectangle:
pos:
rgba:
self.theme_cls.primaryColor \
if self.focus else \
self.theme_cls.transparentColor
SmoothLine:
width: 1
rounded_rectangle:
[ \
self.pos[0] + root.border_width, \
self.pos[1] + self.height/2 + self.border_width * 0.5 \
] if self.orientation == "vertical" else \
[ \
self.pos[0] + root.border_width, \
self.pos[1] + root.border_width \
self.x,
self.y, \
self.width, \
self.height, \
*self.radius, \
]
size:
[ \
self.size[0] - root.border_width * 2, \
self.size[1] / 2 - self.border_width * 1.5 \
] if self.orientation == "vertical" else \
[ \
self.size[0] / 2 - root.border_width * 1.5, \
self.size[1] - root.border_width * 2 \
]
radius:
[root.border_radius, root.border_radius, 0, 0] \
if self.orientation == "vertical" else \
[root.border_radius, 0, 0, root.border_radius]
#PM
Color:
rgba: root._pm_bg_color
RoundedRectangle:
pos:
[ \
self.pos[0] + root.border_width, \
self.pos[1] + self.border_width \
] if self.orientation == "vertical" else \
[ \
self.pos[0] + root.size[0] / 2 + root.border_width / 2, \
self.pos[1] + root.border_width \
]
size:
[ \
self.size[0] - root.border_width * 2, \
self.size[1] / 2 - self.border_width * 1.5 \
] if self.orientation == "vertical" else \
[ \
self.size[0] / 2 - root.border_width * 1.5, \
self.size[1] - root.border_width * 2 \
]
radius:
[0, 0, root.border_radius, root.border_radius] \
if self.orientation == "vertical" else \
[0 ,root.border_radius, root.border_radius, 0]
# AM
AmPmSelectorLabel:
text: "AM"
on_release: root.selected = "am"
text_color: root.text_color
AmPmSelectorLabel:
text: "PM"
on_release: root.selected = "pm"
text_color: root.text_color
<TimeInputTextField>
size_hint: None, 1
width: dp(96)
mode: "fill"
active_line: False
mode: "filled"
font_size: dp(56)
radius: [dp(10), ]
fill_color_normal:
root.parent.parent.parent.accent_color \
if root.parent.parent.parent.accent_color else \
( \
[*root.parent.bg_color_active[:3], 0.5] \
if root.parent.state in ["hour", "minute"] else \
[*root.bg_color[:3], 0.5] \
)
fill_color_focus:
(1, 1, 1, 0.5) \
if root.parent.parent.parent.primary_color else \
self.theme_cls.bg_dark
text_color_focus:
root.parent.parent.parent.accent_color \
if root.parent.parent.parent.accent_color else \
self.theme_cls.primary_color
<TimeInput>
radius: [dp(12), ]
size_hint: None, None
theme_bg_color: "Custom"
fill_color_focus: self.theme_cls.primaryContainerColor
fill_color_normal: self.theme_cls.surfaceContainerHighestColor
width: "96dp"
selection_color: self.theme_cls.transparentColor
-height: "80dp"
-padding: [dp(16 if self.text else 48), dp(6), dp(16), dp(6)]
<MDTimePickerInputContainer>
size_hint_y: None
height: "80dp"
_hour: hour
_minute: minute
TimeInputTextField:
MDTimePickerInputTextField:
id: hour
num_type: "hour"
pos: 0, 0
text_color: root.text_color
disabled: root.disabled
on_text: root.dispatch("on_time_input")
radius: root.hour_radius
readonly: root._readonly
on_text:
root.time_picker._get_time_input(*root.get_time())
root.time_picker.dispatch("on_time_input", "hour", self.text)
on_select:
root.dispatch("on_hour_select")
root.state = "hour"
@ -137,29 +280,100 @@
size: dp(24), dp(80)
halign: "center"
valign: "center"
theme_font_size: "Custom"
font_size: dp(50)
pos: dp(96), 0
theme_text_color: "Custom"
text_color: root.text_color
TimeInputTextField:
MDTimePickerInputTextField:
id: minute
num_type: "minute"
pos: dp(120), 0
text_color: root.text_color
disabled: root.disabled
on_text: root.dispatch("on_time_input")
radius: root.minute_radius
readonly: root._readonly
on_text:
root.time_picker._get_time_input(*root.get_time())
root.time_picker.dispatch("on_time_input", "minute", self.text)
on_select:
root.dispatch("on_minute_select")
root.state = "minute"
<CircularSelector>
<MDTimePickerAmPmSelectorLabel>
halign: "center"
valign: "center"
<MDTimePickerAmPmSelector>
orientation: "vertical"
line_color: self.theme_cls.outlineColor
radius: [dp(12), ]
canvas.before:
# AM
Color:
rgba:
self.theme_cls.tertiaryContainerColor \
if self.selected == "am" else \
self.theme_cls.surfaceContainerHighColor
RoundedRectangle:
pos:
[self.pos[0], self.pos[1] + self.height / 2] \
if self.orientation == "vertical" else \
[self.pos[0], self.pos[1]]
size:
[self.size[0], self.size[1] / 2] \
if self.orientation == "vertical" else \
[self.size[0] / 2, self.size[1]]
radius:
[dp(12), dp(12), 0, 0] \
if self.orientation == "vertical" else \
[dp(12), 0, 0, dp(12)]
# PM
Color:
rgba:
self.theme_cls.tertiaryContainerColor \
if self.selected == "pm" else \
self.theme_cls.surfaceContainerHighColor
RoundedRectangle:
pos:
[self.pos[0], self.pos[1]] \
if self.orientation == "vertical" else \
[self.pos[0] + root.size[0] / 2, self.pos[1]]
size:
[self.size[0], self.size[1] / 2] \
if self.orientation == "vertical" else \
[self.size[0] / 2, self.size[1]]
radius:
[0, 0, dp(12), dp(12)] \
if self.orientation == "vertical" else \
[0, dp(12), dp(12), 0]
MDTimePickerAmPmSelectorLabel:
id: am_label
text: "AM"
on_release: root.selected = "am"
MDDivider:
color: root.theme_cls.outlineColor
orientation:
"vertical" \
if root.orientation == "horizontal" else \
"horizontal"
MDTimePickerAmPmSelectorLabel:
id: pm_label
text: "PM"
on_release: root.selected = "pm"
<MDTimePickerCircularSelectorLabel>
adaptive_size: True
<MDTimePickerCircularSelector>
circular_padding: dp(28)
size_hint: None, None
size: [dp(256), dp(256)]
size: dp(256), dp(256)
row_spacing: dp(40)
on_selector_change: self.time_picker._get_dial_time(self)
canvas.before:
PushMatrix
@ -168,7 +382,7 @@
x: root.scale
y: root.scale
Color:
rgba: root.bg_color
rgba: self.theme_cls.surfaceContainerHighestColor
Ellipse:
size: self.size
pos: self.pos
@ -178,7 +392,7 @@
x: root.content_scale
y: root.content_scale
Color:
rgb: root.selector_color
rgb: self.theme_cls.primaryColor
a: 0 if self.selector_pos == [0, 0] else 1
Ellipse:
size: self.selector_size, self.selector_size
@ -188,153 +402,9 @@
Ellipse:
size: dp(10), dp(10)
pos: [self.center[0] - dp(5), self.center[1] - dp(5)]
Line:
SmoothLine:
points: [self.center, self.selector_pos]
width: dp(1)
canvas.after:
PopMatrix
PopMatrix
<SelectorLabel>
halign: "center"
valign: "center"
adaptive_size: True
theme_text_color: "Custom"
<MDTimePicker>
auto_dismiss: True
size_hint: None, None
_time_input: _time_input
_selector: _selector
_am_pm_selector: _am_pm_selector
_minute_label: _minute_label
_hour_label: _hour_label
MDRelativeLayout:
canvas.before:
Color:
rgba:
root.primary_color \
if root.primary_color \
else root.theme_cls.bg_normal
RoundedRectangle:
size: self.size
radius: root.radius
MDLabel:
id: label_title
font_style: "Body2"
bold: True
theme_text_color: "Custom"
size_hint_x: None
width: root.width
adaptive_height: True
text: root.title
font_name: root.font_name
pos: (dp(24), root.height - self.height - dp(18))
text_color:
root.text_toolbar_color if root.text_toolbar_color \
else root.theme_cls.text_color
TimeInput:
id: _time_input
bg_color:
root.accent_color if root.accent_color else \
root.theme_cls.primary_light
bg_color_active:
root.selector_color if root.selector_color \
else root.theme_cls.primary_color
text_color:
root.input_field_text_color if root.input_field_text_color else \
root.theme_cls.text_color
on_time_input: root._get_time_input(*self.get_time())
on_hour_select: _selector.switch_mode("hour")
on_minute_select: _selector.switch_mode("minute")
minute_radius: root.minute_radius
hour_radius: root.hour_radius
TimeInputLabel:
id: _hour_label
text: "Hour"
opacity: 0
text_color:
root.text_toolbar_color if root.text_toolbar_color else \
root.theme_cls.secondary_text_color
TimeInputLabel:
id: _minute_label
text: "Minute"
opacity: 0
text_color:
root.text_toolbar_color if root.text_toolbar_color else \
root.theme_cls.secondary_text_color
AmPmSelector:
id: _am_pm_selector
owner: root
border_color:
root.accent_color if root.accent_color else \
root.theme_cls.primary_color
border_radius: root.am_pm_radius
bg_color:
root.primary_color if root.primary_color else \
root.theme_cls.bg_normal
border_width: root.am_pm_border_width
bg_color_active:
root.selector_color if root.selector_color else \
root.theme_cls.primary_light
text_color:
root.input_field_text_color if root.input_field_text_color else \
root.theme_cls.text_color
on_selected: root._get_am_pm(self.selected)
CircularSelector:
id: _selector
text_color:
root.text_color if root.text_color else \
root.theme_cls.text_color
bg_color:
root.accent_color if root.accent_color else \
root.theme_cls.primary_light
selector_color:
root.primary_color if root.primary_color else \
root.theme_cls.primary_color
font_name: root.font_name
on_selector_change: root._get_dial_time(_selector)
MDIconButton:
id: input_clock_switch
icon: "keyboard"
pos: dp(12), dp(8)
theme_icon_color: "Custom"
icon_size: "24dp"
on_release: root._switch_input()
icon_color:
root.text_toolbar_color if root.text_toolbar_color else \
root.theme_cls.secondary_text_color
MDFlatButton:
id: cancel_button
text: "CANCEL"
on_release: root.dispatch("on_cancel", None)
theme_text_color: "Custom"
pos: root.width - self.width - ok_button.width - dp(10), dp(10)
font_name: root.font_name
text_color:
root.theme_cls.primary_color \
if not root.text_button_color else root.text_button_color
MDFlatButton:
id: ok_button
width: dp(32)
pos: root.width - self.width, dp(10)
text: "OK"
theme_text_color: "Custom"
font_name: root.font_name
text_color:
root.theme_cls.primary_color \
if not root.text_button_color else root.text_button_color
on_release: root.dispatch("on_save", root._get_data())