first commit

This commit is contained in:
Yura 2024-09-15 15:12:16 +03:00
commit 417e54da96
5696 changed files with 900003 additions and 0 deletions

View file

@ -0,0 +1 @@
from .timepicker import MDTimePicker # NOQA F401

View file

@ -0,0 +1,340 @@
<TimeInputLabel@MDLabel>:
theme_text_color: "Custom"
font_size: dp(10)
halign: "left"
valign: "bottom"
adaptive_size: True
<AmPmSelectorLabel>
halign: "center"
valign: "center"
theme_text_color: "Custom"
<AmPmSelector>
size_hint: None, None
canvas.before:
Color:
rgba: root.border_color
RoundedRectangle:
pos: self.pos
size: self.size
radius: [root.border_radius, ]
#AM
Color:
rgba: root._am_bg_color
RoundedRectangle:
pos:
[ \
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 \
]
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
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>
size_hint: None, None
_hour: hour
_minute: minute
TimeInputTextField:
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
on_select:
root.dispatch("on_hour_select")
root.state = "hour"
MDLabel:
text: ":"
size_hint: None, None
size: dp(24), dp(80)
halign: "center"
valign: "center"
font_size: dp(50)
pos: dp(96), 0
theme_text_color: "Custom"
text_color: root.text_color
TimeInputTextField:
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
on_select:
root.dispatch("on_minute_select")
root.state = "minute"
<CircularSelector>
circular_padding: dp(28)
size_hint: None, None
size: [dp(256), dp(256)]
row_spacing: dp(40)
canvas.before:
PushMatrix
Scale:
origin: self.scale_origin
x: root.scale
y: root.scale
Color:
rgba: root.bg_color
Ellipse:
size: self.size
pos: self.pos
PushMatrix
Scale:
origin: self.center
x: root.content_scale
y: root.content_scale
Color:
rgb: root.selector_color
a: 0 if self.selector_pos == [0, 0] else 1
Ellipse:
size: self.selector_size, self.selector_size
pos:
[self.selector_pos[0] - self.selector_size / 2, \
self.selector_pos[1] - self.selector_size / 2]
Ellipse:
size: dp(10), dp(10)
pos: [self.center[0] - dp(5), self.center[1] - dp(5)]
Line:
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())

View file

@ -0,0 +1,832 @@
"""
Components/TimePicker
=====================
.. seealso::
`Material Design spec, Time picker <https://material.io/components/time-pickers>`_
.. rubric:: Includes time picker.
.. 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::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
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()
'''
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_time_picker(self):
'''Open time picker dialog.'''
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()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDTimePicker.png
:align: center
Binding method returning set time
---------------------------------
.. code-block:: python
def show_time_picker(self):
time_dialog = MDTimePicker()
time_dialog.bind(time=self.get_time)
time_dialog.open()
def get_time(self, instance, time):
'''
The method returns the set time.
:type instance: <kivymd.uix.picker.MDTimePicker object>
:type time: <class 'datetime.time'>
'''
return time
Open time dialog with the specified time
----------------------------------------
Use the :attr:`~MDTimePicker.set_time` method of the
:class:`~MDTimePicker.` class.
.. code-block:: python
def show_time_picker(self):
from datetime import datetime
# Must be a datetime object
previous_time = datetime.strptime("03:20:00", '%H:%M:%S').time()
time_dialog = MDTimePicker()
time_dialog.set_time(previous_time)
time_dialog.open()
.. note:: For customization of the :class:`~MDTimePicker` class, see the
documentation in the :class:`~kivymd.uix.pickers.datepicker.datepicker.BaseDialogPicker` class.
.. code-block:: python
MDTimePicker(
primary_color="brown",
accent_color="red",
text_button_color="white",
).open()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-customization.png
:align: center
"""
__all__ = ("MDTimePicker",)
import datetime
import os
import re
import time
from typing import List, Union
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.event import EventDispatcher
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
BooleanProperty,
ColorProperty,
ListProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
VariableListProperty,
)
from kivy.uix.behaviors import ButtonBehavior
from kivy.vector import Vector
from kivymd import uix_path
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.circularlayout import MDCircularLayout
from kivymd.uix.label import MDLabel
from kivymd.uix.pickers.datepicker import BaseDialogPicker
from kivymd.uix.relativelayout import MDRelativeLayout
from kivymd.uix.textfield import MDTextField
with open(
os.path.join(uix_path, "pickers", "timepicker", "timepicker.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class AmPmSelectorLabel(ButtonBehavior, MDLabel):
pass
class AmPmSelector(MDBoxLayout):
border_radius = NumericProperty()
border_color = ColorProperty()
bg_color = ColorProperty()
bg_color_active = ColorProperty()
border_width = NumericProperty()
am = ObjectProperty()
am = ObjectProperty()
owner = ObjectProperty()
text_color = ColorProperty()
selected = StringProperty()
_am_bg_color = ColorProperty()
_pm_bg_color = ColorProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(selected=self._upadte_color)
Clock.schedule_once(self._upadte_color)
def _upadte_color(self, *args):
bg_color = (
self.owner.accent_color
if self.owner.accent_color
else self.bg_color_active
)
if self.selected == "am":
self._am_bg_color = bg_color
self._pm_bg_color = (
self.owner.primary_color
if self.owner.accent_color
else self.bg_color
)
elif self.selected == "pm":
self._am_bg_color = (
self.owner.primary_color
if self.owner.accent_color
else self.bg_color
)
self._pm_bg_color = bg_color
class TimeInputTextField(MDTextField):
num_type = OptionProperty("hour", options=["hour", "minute"])
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)
Clock.schedule_once(self.set_text)
self.register_event_type("on_select")
self.bind(text_color_focus=self.setter("hint_text_color_normal"))
def validate_time(self, text) -> Union[None, re.Match]:
reg = self.hour_regx if self.num_type == "hour" else self.minute_regx
return re.match(reg, text)
def insert_text(self, text, from_undo=False):
strip_text = self.text.strip()
current_string = "".join([strip_text, text])
if not self.validate_time(current_string):
text = ""
return super().insert_text(text, from_undo=from_undo)
def set_text(self, *args) -> None:
"""
Texts should be center aligned. Now we are setting the padding of text
to somehow make them aligned.
"""
def set_text(*args):
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]
if len(self.text) > 1:
self.text = self.text.replace(" ", "")
Clock.schedule_once(set_text)
def on_focus(self, *args) -> None:
super().on_focus(*args)
if self.text.strip():
if (
not self.focus
and int(self.text) == 0
and self.num_type == "hour"
):
self.text = "12"
else:
self.text = " 12" if self.num_type == "hour" else " 00"
def on_select(self, *args) -> None:
pass
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.dispatch("on_select")
super().on_touch_down(touch)
class TimeInput(MDRelativeLayout):
"""Implements two text fields for displaying and entering a time value."""
bg_color = ColorProperty()
bg_color_active = ColorProperty()
text_color = ColorProperty()
disabled = BooleanProperty(True)
minute_radius = ListProperty([0, 0, 0, 0])
hour_radius = ListProperty([0, 0, 0, 0])
state = StringProperty("hour")
_hour = ObjectProperty()
_minute = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_time_input")
self.register_event_type("on_hour_select")
self.register_event_type("on_minute_select")
def set_time(self, time_list) -> None:
hour, minute = time_list
self._hour.text = hour
self._minute.text = minute
def get_time(self) -> List[str]:
hour = self._hour.text.strip()
minute = self._minute.text.strip()
return [hour, minute]
def on_time_input(self, *args) -> None:
pass
def on_minute_select(self, *args) -> None:
pass
def on_hour_select(self, *args) -> None:
pass
def _update_padding(self, *args):
self._hour.set_text()
self._minute.set_text()
class SelectorLabel(MDLabel):
pass
class CircularSelector(MDCircularLayout, EventDispatcher):
"""Implements clock face display."""
mode = OptionProperty("hour", options=["hour", "minute"]) # and military
text_color = ColorProperty()
selected_hour = StringProperty("12")
selected_minute = StringProperty("0")
selector_size = NumericProperty("48dp")
selector_pos = ListProperty([0, 0])
selector_color = ColorProperty()
bg_color = ColorProperty()
font_name = StringProperty()
scale = NumericProperty(1)
content_scale = NumericProperty(1)
t = StringProperty("out_quad")
d = NumericProperty(0.2)
scale_origin = ListProperty([100, 100])
_centers_pos = ListProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(
mode=self._update_labels,
selected_hour=self.update_time,
selected_minute=self.update_time,
)
Clock.schedule_once(lambda x: self._update_labels(animate=False))
self.register_event_type("on_selector_change")
def do_layout(self, *largs, **kwargs):
self.update_time()
return super().do_layout(*largs, **kwargs)
def set_selector(self, selected) -> bool:
"""Sets the selector's position towards the given text."""
widget = None
for wid in self.children:
wid.text_color = self.text_color
if wid.text == selected:
widget = wid
if not widget:
return False
self.selector_pos = widget.center
widget.text_color = [1, 1, 1, 1]
self.dispatch("on_selector_change")
return True
def set_time(self, selected) -> None:
if self.mode == "hour":
self.selected_hour = selected
elif self.mode == "minute":
self.selected_minute = selected
def update_time(self, *args) -> None:
if self.mode == "hour":
self.set_selector(self.selected_hour)
elif self.mode == "minute":
self.set_selector(self.selected_minute)
def get_selected(self) -> str:
return self.selected
def switch_mode(self, mode) -> None:
if mode != self.mode:
self.mode = mode
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
touch.grab(self)
closest_wid = self._get_closest_widget(touch.pos)
self.set_time(closest_wid.text)
return True
def on_touch_move(self, touch):
if touch.grab_current == self:
closest_wid = self._get_closest_widget(touch.pos)
self.set_time(closest_wid.text)
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
return True
def on_selector_change(self, *args):
pass
def _update_labels(self, animate=True, *args):
"""
This method builds the selector based on current mode which currently
can be hour or minute.
"""
if self.mode == "hour":
param = (1, 12)
self.degree_spacing = 30
self.start_from = 60
elif self.mode == "minute":
param = (0, 59, 5)
self.degree_spacing = 6
self.start_from = 90
elif self.mode == "military":
param = (1, 24)
self.degree_spacing = 30
self.start_from = 90
if animate:
anim = Animation(content_scale=0, t=self.t, d=self.d)
anim.bind(on_complete=lambda *args: self._add_items(*param))
anim.start(self)
else:
self._add_items(*param)
def _add_items(self, start, end, step=1):
"""
Adds all number in range `[start, end + 1]` to the circular layout with
the specified step. Step means that all widgets will be added to layout
but sets the opacity for skipped widgets to `0` because we are using
the label's text as a reference to the selected number so we have to
add these to layout.
"""
self.clear_widgets()
i = 0
for x in range(start, end + 1):
label = SelectorLabel(
text=f"{x}",
)
if i % step != 0:
label.opacity = 0
self.bind(
text_color=label.setter("text_color"),
font_name=label.setter("font_name"),
)
self.add_widget(label)
i += 1
Clock.schedule_once(self.update_time)
Clock.schedule_once(self._get_centers, 0.1)
anim = Animation(content_scale=1, t=self.t, d=self.d)
anim.start(self)
def _get_centers(self, *args):
"""
Returns a list of all center. we use this for positioning the selector
indicator.
"""
self._centers_pos = []
for child in self.children:
self._centers_pos.append(child.center)
def _get_closest_widget(self, pos):
"""
Returns the nearest widget to the given position. we use this to create
the magnetic effect.
"""
distance = [Vector(pos).distance(point) for point in self._centers_pos]
if not distance:
return False
index = distance.index(min(distance))
return self.children[index]
class MDTimePicker(BaseDialogPicker):
hour = StringProperty("12")
"""
Current hour.
:attr:`hour` is an :class:`~kivy.properties.StringProperty`
and defaults to `'12'`.
"""
minute = StringProperty("0")
"""
Current minute.
:attr:`minute` is an :class:`~kivy.properties.StringProperty`
and defaults to `0`.
"""
minute_radius = VariableListProperty(dp(5), length=4)
"""
Radius of the minute input field.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-minute-radius.png
:align: center
:attr:`minute_radius` is an :class:`~kivy.properties.ListProperty`
and defaults to `[dp(5), dp(5), dp(5), dp(5)]`.
"""
hour_radius = VariableListProperty(dp(5), length=4)
"""
Radius of the hour input field.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-hour-radius.png
:align: center
:attr:`hour_radius` is an :class:`~kivy.properties.ListProperty`
and defaults to `[dp(5), dp(5), dp(5), dp(5)]`.
"""
am_pm_radius = NumericProperty("5dp")
"""
Radius of the AM/PM selector.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-am-pm-radius.png
:align: center
:attr:`am_pm_radius` is an :class:`~kivy.properties.NumericProperty`
and defaults to `dp(5)`.
"""
am_pm_border_width = NumericProperty("1dp")
"""
Width of the AM/PM selector's borders.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/time-picker-am-pm-border-width.png
:align: center
:attr:`am_pm_border_width` is an :class:`~kivy.properties.NumericProperty`
and defaults to `dp(1)`.
"""
am_pm = OptionProperty("am", options=["am", "pm"])
"""
Current AM/PM mode.
:attr:`am_pm` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'am'`.
"""
animation_duration = NumericProperty(0.3)
"""
Duration of the animations.
:attr:`animation_duration` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
animation_transition = StringProperty("out_quad")
"""
Transition type of the animations.
:attr:`animation_transition` is an :class:`~kivy.properties.StringProperty`
and defaults to `'out_quad'`.
"""
time = ObjectProperty(allownone=True)
"""
Returns the current time object.
:attr:`time` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
_state = StringProperty()
_selector = ObjectProperty()
_time_input = ObjectProperty()
_am_pm_selector = ObjectProperty()
_hour_label = ObjectProperty()
_minute_label = ObjectProperty()
_anim_playing = BooleanProperty(False)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(
hour=self._set_current_time,
minute=self._set_current_time,
am_pm=self._set_current_time,
)
self.theme_cls.bind(device_orientation=self._check_orienation)
if self.title == "SELECT DATE":
self.title = "SELECT TIME"
self.set_time(datetime.time(hour=12, minute=0)) # default time
self._check_orienation()
def set_time(self, time_obj) -> None:
"""Manually set time dialog with the specified time."""
hour = time_obj.hour
minute = time_obj.minute
if hour > 12:
hour -= 12
mode = "pm"
else:
mode = "am"
hour = str(hour)
minute = str(minute)
self._set_time_input(hour, minute)
self._set_dial_time(hour, minute)
self._set_am_pm(mode)
def get_state(self) -> str:
"""
Returns the current state of TimePicker.
Can be one of `portrait`, `landscape` or `input`.
"""
return self._state
def _get_dial_time(self, instance):
mode = instance.mode
if mode == "hour":
self.hour = instance.selected_hour
elif mode == "minute":
self.minute = instance.selected_minute
else:
raise Exception("invalid mode for MDTimePicker: " % mode)
self._set_time_input(self.hour, self.minute)
def _set_dial_time(self, hour, minute):
self._selector.selected_minute = minute
self._selector.selected_hour = hour
def _get_time_input(self, hour, minute):
if hour:
self.hour = f"{int(hour):01d}"
if minute:
self.minute = f"{int(minute):01d}"
self._set_dial_time(self.hour, self.minute)
def _set_time_input(self, hour, minute):
hour = f"{int(hour):02d}"
minute = f"{int(minute):02d}"
if self._state != "input":
self._time_input.set_time([hour, minute])
def _get_am_pm(self, selected):
self.am_pm = selected
def _set_am_pm(self, selected: str) -> None:
"""Used by set_time() to manually set the mode to "am" or "pm"."""
self.am_pm = selected
self._am_pm_selector.mode = self.am_pm
self._am_pm_selector.selected = self.am_pm
def _get_data(self):
try:
if time.strftime("%p"):
result = datetime.datetime.strptime(
f"{int(self.hour):02d}:{int(self.minute):02d} {self.am_pm}",
"%I:%M %p",
).time()
else:
result = datetime.datetime.strptime(
f"{int(self.hour):02d}:{int(self.minute):02d}",
"%I:%M",
).time()
return result
except ValueError:
return None # hour is zero
def _check_orienation(self, *args, do_anim=False):
orientation = self.theme_cls.device_orientation
if self._state != "input" and orientation != self._state:
self._update_pos_size(orientation, anim=do_anim)
def _update_pos_size(self, orientation, anim=False):
d = self.animation_duration
# time input
time_input_pos = (
[dp(24), dp(368)]
if orientation == "portrait"
else (
[dp(24), dp(178)]
if orientation == "landscape"
else [dp(24), dp(96)]
)
)
if anim:
_time_input = Animation(
pos=time_input_pos,
d=d,
t=self.animation_transition, # 80 - 8,
)
_time_input.start(self._time_input)
else:
self._time_input.pos = time_input_pos
self._time_input.disabled = False if orientation == "input" else True
self._time_input.size = (
[dp(216), dp(62)] if orientation == "input" else [dp(216), dp(72)]
)
Clock.schedule_once(self._time_input._update_padding)
# Circular selector.
if orientation == "input":
if self.theme_cls.device_orientation == "portrait":
selector_pos = [dp(34), dp(-256)]
self._selector.scale_origin = [dp(162), dp(200)]
else:
selector_pos = [dp(324), dp(-19)]
self._selector.scale_origin = [dp(292), dp(109)]
elif orientation == "portrait":
self._selector.pos = selector_pos = [dp(36), dp(76)]
else:
self._selector.pos = selector_pos = [dp(304), dp(76)]
Animation(
pos=selector_pos,
scale=0 if orientation == "input" else 1,
opacity=0 if orientation == "input" else 1,
d=d,
t=self.animation_transition,
).start(self._selector)
# AM/PM selector.
am_pm_pos = (
[dp(252), dp(368)]
if orientation == "portrait"
else (
[dp(24), dp(126)]
if orientation == "landscape"
else [dp(252), dp(96)]
)
)
am_pm_size = (
[dp(52), dp(80)]
if orientation == "portrait"
else (
[dp(216), dp(40)]
if orientation == "landscape"
else [dp(48), dp(70)]
)
)
if anim:
Animation(
pos=am_pm_pos,
size=am_pm_size,
d=d,
t=self.animation_transition,
).start(self._am_pm_selector)
else:
self._am_pm_selector.pos = am_pm_pos
self._am_pm_selector.size = am_pm_size
self._am_pm_selector.orientation = (
"horizontal" if orientation == "landscape" else "vertical"
)
# MDTimePicker.
time_picker_size = (
[dp(328), dp(500)]
if orientation == "portrait"
else (
[dp(584), dp(368)]
if orientation == "landscape"
else [dp(324), dp(218)]
)
)
if anim:
Animation(
size=time_picker_size,
d=d,
t=self.animation_transition,
).start(self)
else:
self.size = time_picker_size
# Minute label.
Animation(
pos=[dp(144), dp(76)],
opacity=1 if orientation == "input" else 0,
d=d,
t=self.animation_transition,
).start(self._minute_label)
# Hour label.
Animation(
pos=[dp(24), dp(76)],
opacity=1 if orientation == "input" else 0,
d=d,
t=self.animation_transition,
).start(self._hour_label)
self._state = orientation
self.ids.input_clock_switch.icon = (
"clock-time-four-outline" if orientation == "input" else "keyboard"
)
def _set_current_time(self, *args):
self.time = self._get_data()
def _switch_input(self):
self._update_pos_size(
self.theme_cls.device_orientation
if self._state == "input"
else "input",
anim=True,
)