2022-07-07 22:16:10 +02:00
"""
Components / DatePicker
== == == == == == == == == == =
. . seealso : :
` Material Design spec , Date picker < https : / / material . io / components / date - pickers > ` _
. . rubric : : Includes date picker .
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / picker - previous . png
: align : center
2022-10-03 00:46:03 +02:00
. . rubric : : Usage
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
. . tabs : :
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
. . tab : : Declarative KV style
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
. . code - block : : python
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
from kivy . lang import Builder
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
from kivymd . app import MDApp
from kivymd . uix . pickers import MDDatePicker
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
KV = '''
MDFloatLayout :
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
MDRaisedButton :
text : " Open date picker "
pos_hint : { ' center_x ' : .5 , ' center_y ' : .5 }
on_release : app . show_date_picker ( )
'''
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
class Test ( MDApp ) :
def build ( self ) :
self . theme_cls . theme_style = " Dark "
self . theme_cls . primary_palette = " Orange "
return Builder . load_string ( KV )
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
def on_save ( self , instance , value , date_range ) :
'''
Events called when the " OK " dialog box button is clicked .
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
: 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 ' >;
'''
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
print ( instance , value , date_range )
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
def on_cancel ( self , instance , value ) :
''' Events called when the " CANCEL " dialog box button is clicked. '''
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
def show_date_picker ( self ) :
date_dialog = MDDatePicker ( )
date_dialog . bind ( on_save = self . on_save , on_cancel = self . on_cancel )
date_dialog . open ( )
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
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
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 ( )
2022-07-07 22:16:10 +02:00
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
Test ( ) . run ( )
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / MDDatePicker . png
2022-07-07 22:16:10 +02:00
: align : center
Open date dialog with the specified date
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
. . code - block : : python
def show_date_picker ( self ) :
date_dialog = MDDatePicker ( year = 1983 , month = 4 , day = 12 )
date_dialog . open ( )
2022-10-08 17:17:59 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / specified - date . png
2022-07-07 22:16:10 +02:00
: align : center
Interval date
- - - - - - - - - - - - -
You can set the time interval from and to the set date . All days of the week
that are not included in this range will have the status ` disabled ` .
. . code - block : : python
def show_date_picker ( self ) :
date_dialog = MDDatePicker (
2022-10-08 17:17:59 +02:00
min_date = datetime . date . today ( ) ,
max_date = datetime . date (
datetime . date . today ( ) . year ,
datetime . date . today ( ) . month ,
datetime . date . today ( ) . day + 2 ,
) ,
2022-07-07 22:16:10 +02:00
)
date_dialog . open ( )
2022-10-08 17:17:59 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / range - date . png
2022-07-07 22:16:10 +02:00
: align : center
The range of available dates can be changed in the picker dialog :
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / change - range - date . gif
: align : center
Select year
- - - - - - - - - - -
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / select - year - date . gif
: align : center
. . warning : : The list of years when opening is not automatically set
to the current year .
You can set the range of years using the : attr : ` ~ kivymd . uix . picker . MDDatePicker . min_year ` and
: attr : ` ~ kivymd . uix . picker . MDDatePicker . max_year ` attributes :
. . code - block : : python
def show_date_picker ( self ) :
2022-10-08 17:17:59 +02:00
date_dialog = MDDatePicker ( min_year = 2022 , max_year = 2030 )
2022-07-07 22:16:10 +02:00
date_dialog . open ( )
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / min - max - year - date . png
: align : center
Set and select a date range
- - - - - - - - - - - - - - - - - - - - - - - - - - -
. . code - block : : python
def show_date_picker ( self ) :
date_dialog = MDDatePicker ( mode = " range " )
date_dialog . open ( )
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / set - select - range - date . gif
: align : center
"""
2022-10-08 17:17:59 +02:00
from __future__ import annotations
2022-07-07 22:16:10 +02:00
__all__ = ( " MDDatePicker " , " BaseDialogPicker " , " DatePickerInputField " )
import calendar
import datetime
2022-10-08 17:17:59 +02:00
import math
2022-07-07 22:16:10 +02:00
import os
import time
from datetime import date
2022-10-08 17:17:59 +02:00
from itertools import zip_longest
2022-07-07 22:16:10 +02:00
from typing import Union
from kivy import Logger
from kivy . animation import Animation
from kivy . lang import Builder
from kivy . metrics import dp
from kivy . properties import (
BooleanProperty ,
ColorProperty ,
ListProperty ,
NumericProperty ,
ObjectProperty ,
OptionProperty ,
StringProperty ,
)
from kivy . uix . anchorlayout import AnchorLayout
from kivy . uix . behaviors import ButtonBehavior , FocusBehavior
from kivy . uix . recyclegridlayout import RecycleGridLayout
from kivy . uix . recycleview . layout import LayoutSelectionBehavior
from kivy . uix . recycleview . views import RecycleDataViewBehavior
from kivymd import uix_path
from kivymd . theming import ThemableBehavior , ThemeManager
from kivymd . toast import toast
from kivymd . uix . behaviors import (
CircularRippleBehavior ,
2022-10-08 17:17:59 +02:00
CommonElevationBehavior ,
2022-07-07 22:16:10 +02:00
SpecificBackgroundColorBehavior ,
)
from kivymd . uix . boxlayout import MDBoxLayout
from kivymd . uix . button import MDIconButton
from kivymd . uix . dialog import BaseDialog
from kivymd . uix . label import MDLabel
from kivymd . uix . textfield import MDTextField
from kivymd . uix . tooltip import MDTooltip
with open (
os . path . join ( uix_path , " pickers " , " datepicker " , " datepicker.kv " ) ,
encoding = " utf-8 " ,
) as kv_file :
Builder . load_string ( kv_file . read ( ) )
class BaseDialogPicker (
BaseDialog ,
2022-10-08 17:17:59 +02:00
CommonElevationBehavior ,
2022-07-07 22:16:10 +02:00
SpecificBackgroundColorBehavior ,
) :
"""
Base class for : class : ` ~ kivymd . uix . picker . MDDatePicker ` and
: class : ` ~ kivymd . uix . picker . MDTimePicker ` classes .
: Events :
` on_save `
Events called when the " OK " dialog box button is clicked .
` on_cancel `
Events called when the " CANCEL " dialog box button is clicked .
"""
title_input = StringProperty ( " INPUT DATE " )
"""
Dialog title fot input date .
. . code - block : : python
MDDatePicker ( title_input = " INPUT DATE " )
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / md - date - picker - input - date . png
: align : center
: attr : ` title_input ` is an : class : ` ~ kivy . properties . StringProperty `
and defaults to ` INPUT DATE ` .
"""
title = StringProperty ( " SELECT DATE " )
"""
Dialog title fot select date .
. . code - block : : python
MDDatePicker ( title = " SELECT DATE " )
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / md - date - picker - select - date . png
: align : center
: attr : ` title ` is an : class : ` ~ kivy . properties . StringProperty `
and defaults to ` SELECT DATE ` .
"""
radius = ListProperty ( [ 7 , 7 , 7 , 7 ] )
"""
Radius list for the four corners of the dialog .
. . code - block : : python
MDDatePicker ( radius = [ 7 , 7 , 7 , 26 ] )
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / md - date - picker - radius . png
: align : center
: attr : ` radius ` is an : class : ` ~ kivy . properties . ListProperty `
and defaults to ` [ 7 , 7 , 7 , 7 ] ` .
"""
primary_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Background color of toolbar in ( r , g , b , a ) or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
2022-10-08 17:17:59 +02:00
MDDatePicker ( primary_color = " brown " )
2022-07-07 22:16:10 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / primary - color - date . png
: align : center
: attr : ` primary_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
accent_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Background color of calendar / clock face in ( r , g , b , a ) or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / accent - color - date . png
: align : center
: attr : ` accent_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
selector_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Background color of the selected day of the month or hour in ( r , g , b , a )
or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
selector_color = " red " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / selector - color - date . png
: align : center
: attr : ` selector_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
text_toolbar_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Color of labels for text on a toolbar in ( r , g , b , a ) or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
selector_color = " red " ,
text_toolbar_color = " lightgrey " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / text - toolbar - color - date . png
: align : center
: attr : ` text_toolbar_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
text_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Color of text labels in calendar / clock face in ( r , g , b , a ) or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
selector_color = " red " ,
text_toolbar_color = " lightgrey " ,
text_color = " orange " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / text - color - date . png
: align : center
: attr : ` text_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
text_current_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Color of the text of the current day of the month / hour in ( r , g , b , a )
or string format .
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
selector_color = " red " ,
text_toolbar_color = " lightgrey " ,
text_color = " orange " ,
text_current_color = " white " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / text - current - color - date . png
: align : center
: attr : ` text_current_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
text_button_color = ColorProperty ( None )
"""
Text button color in ( r , g , b , a ) format .
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
primary_color = " brown " ,
accent_color = " darkred " ,
selector_color = " red " ,
text_toolbar_color = " lightgrey " ,
text_color = " orange " ,
text_current_color = " white " ,
text_button_color = " lightgrey " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / text - button - color - date . png
: align : center
: attr : ` text_button_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
2022-10-08 17:17:59 +02:00
input_field_background_color_normal = ColorProperty ( None )
2022-07-07 22:16:10 +02:00
"""
2022-10-08 17:17:59 +02:00
Background color normal of input fields in ( r , g , b , a ) or string format .
. . versionadded : : 1.1 .0
2022-07-07 22:16:10 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
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 " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / input - field - background - color - date . png
: align : center
2022-10-08 17:17:59 +02:00
: attr : ` input_field_background_color_normal ` is an : class : ` ~ kivy . properties . ColorProperty `
2022-07-07 22:16:10 +02:00
and defaults to ` None ` .
"""
2022-10-08 17:17:59 +02:00
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 .
"""
2022-07-07 22:16:10 +02:00
input_field_text_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
. . deprecated : : 1.1 .0
Use : attr : ` input_field_text_color_normal ` instead .
"""
2022-10-02 17:16:59 +02:00
2022-10-08 17:17:59 +02:00
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
2022-10-02 17:16:59 +02:00
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
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 " ,
2022-10-02 17:16:59 +02:00
)
2022-10-08 17:17:59 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / input - field - text - color - normal - date . png
2022-10-02 17:16:59 +02:00
: align : center
2022-10-08 17:17:59 +02:00
: 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 `
2022-07-07 22:16:10 +02:00
and defaults to ` None ` .
"""
font_name = StringProperty ( " Roboto " )
"""
Font name for dialog window text .
. . code - block : : python
MDDatePicker (
2022-10-08 17:17:59 +02:00
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 " ,
2022-07-07 22:16:10 +02:00
)
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / font - name - date . png
: align : center
: attr : ` font_name ` is an : class : ` ~ kivy . properties . StringProperty `
and defaults to ` ' Roboto ' ` .
"""
def __init__ ( self , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . register_event_type ( " on_save " )
self . register_event_type ( " on_cancel " )
2022-10-08 17:17:59 +02:00
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
2022-07-07 22:16:10 +02:00
def on_save ( self , * args ) - > None :
""" Events called when the " OK " dialog box button is clicked. """
self . dismiss ( )
def on_cancel ( self , * args ) - > None :
""" Events called when the " CANCEL " dialog box button is clicked. """
self . dismiss ( )
class DatePickerBaseTooltip ( MDTooltip ) :
""" Implements tooltips for members of the :class:`~MDDatePicker` class. """
owner = ObjectProperty ( ) # MDDatePicker object
hint_text = StringProperty ( )
class DatePickerIconTooltipButton ( MDIconButton , DatePickerBaseTooltip ) :
pass
class DatePickerWeekdayLabel ( MDLabel , DatePickerBaseTooltip ) :
pass
class DatePickerTypeDateError ( Exception ) :
pass
class DatePickerInputField ( MDTextField ) :
""" Implements date input in dd/mm/yyyy format. """
helper_text_mode = StringProperty ( " on_error " )
owner = ObjectProperty ( ) # MDDatePicker object
def set_error ( self ) :
""" Sets a text field to an error state. """
self . error = True
def input_filter ( self , value : str , boolean : bool ) - > Union [ str , None ] :
""" Filters the input according to the specified mode. """
if self . is_numeric ( value ) :
return value
def is_numeric ( self , value : str ) - > bool :
"""
Returns true if the value of the ` value ` argument can be converted
to an integer , or if the value of the ` value ` argument is ' / ' .
"""
try :
if value == " / " :
return True
int ( value )
return True
except ValueError :
return False
def get_list_date ( self ) - > list :
"""
Returns a list as ` [ dd , mm , yyyy ] ` from a text fied for entering a date .
"""
return [ d for d in self . text . split ( " / " ) if d ]
class DatePickerInputFieldContainer ( MDBoxLayout ) :
owner = ObjectProperty ( ) # MDDatePicker object
class SelectYearList ( FocusBehavior , LayoutSelectionBehavior , RecycleGridLayout ) :
""" A class that implements a list for choosing a year. """
class DatePickerDaySelectableItem (
ThemableBehavior , CircularRippleBehavior , ButtonBehavior , AnchorLayout
) :
""" A class that implements a list for choosing a day. """
text = StringProperty ( )
owner = ObjectProperty ( )
is_today = BooleanProperty ( False )
is_selected = BooleanProperty ( False )
current_month = NumericProperty ( )
current_year = NumericProperty ( )
index = NumericProperty ( 0 )
def check_date ( self , year : int , month : int , day : int ) :
try :
return date ( year , month , day ) in self . owner . _date_range
except ValueError as error :
if str ( error ) == " day is out of range for month " :
return False
def on_release ( self ) :
if (
self . owner . mode == " range "
and self . owner . _end_range_date
and self . owner . _start_range_date
) :
return
if (
not self . owner . _input_date_dialog_open
and not self . owner . _select_year_dialog_open
) :
if self . owner . mode == " range " and not self . owner . _start_range_date :
self . owner . _start_range_date = date (
self . current_year , self . current_month , int ( self . text )
)
self . owner . min_date = self . owner . _start_range_date
elif (
self . owner . mode == " range "
and not self . owner . _end_range_date
and self . owner . _start_range_date
) :
self . owner . _end_range_date = date (
self . current_year , self . current_month , int ( self . text )
)
if self . owner . _end_range_date < = self . owner . min_date :
toast ( self . owner . date_range_text_error )
Logger . error (
" `Data Picker: max_date` value cannot be less than "
" or equal to ' min_date ' value. "
)
self . owner . _start_range_date = 0
self . owner . _end_range_date = 0
return
self . owner . max_date = self . owner . _end_range_date
self . owner . update_calendar_for_date_range ( )
self . owner . set_selected_widget ( self )
2022-10-08 17:17:59 +02:00
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 )
2022-07-07 22:16:10 +02:00
class DatePickerYearSelectableItem ( RecycleDataViewBehavior , MDLabel ) :
""" Implements an item for a pick list of the year. """
index = None
selected_color = ColorProperty ( [ 0 , 0 , 0 , 0 ] )
owner = ObjectProperty ( )
def refresh_view_attrs ( self , rv , index , data ) :
self . index = index
return super ( ) . refresh_view_attrs ( rv , index , data )
def on_touch_down ( self , touch ) :
if super ( ) . on_touch_down ( touch ) :
return True
2022-10-08 17:17:59 +02:00
if self . collide_point ( * touch . pos ) :
2022-07-07 22:16:10 +02:00
self . owner . year = int ( self . text )
# self.owner.sel_year = self.owner.year
self . owner . ids . label_full_date . text = self . owner . set_text_full_date (
self . owner . sel_year ,
self . owner . sel_month ,
self . owner . sel_day ,
self . owner . theme_cls . device_orientation ,
)
return self . parent . select_with_touch ( self . index , touch )
def apply_selection ( self , table_data , index , is_selected ) :
if is_selected :
self . selected_color = (
self . owner . selector_color
if self . owner . selector_color
else self . theme_cls . primary_color
)
self . text_color = ( 1 , 1 , 1 , 1 )
else :
if int ( self . text ) == self . owner . sel_year :
self . text_color = (
self . theme_cls . primary_color
if not self . owner . text_current_color
else self . owner . text_current_color
)
self . selected_color = [ 0 , 0 , 0 , 0 ]
self . text_color = ( 0 , 0 , 0 , 1 )
# TODO: Add the feature to embed the `MDDatePicker` class in other layouts
# and not use it as a modal dialog.
# Add a date input mask. Currently, the date is entered in the format
# 'dd/mm/yy'. In some countries, the date is formatted as 'mm/dd/yy'.
class MDDatePicker ( BaseDialogPicker ) :
text_weekday_color = ColorProperty ( None )
"""
2022-10-08 17:17:59 +02:00
Text color of weekday names in ( r , g , b , a ) or string format .
2022-07-07 22:16:10 +02:00
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / md - date - picker - text - weekday - color . png
: align : center
: attr : ` text_weekday_color ` is an : class : ` ~ kivy . properties . ColorProperty `
and defaults to ` None ` .
"""
helper_text = StringProperty ( " Wrong date " )
"""
Helper text when entering an invalid date .
. . image : : https : / / github . com / HeaTTheatR / KivyMD - data / raw / master / gallery / kivymddoc / md - date - picker - helper - text . png
: align : center
: attr : ` helper_text ` is an : class : ` ~ kivy . properties . StringProperty `
and defaults to ` ' Wrong date ' ` .
"""
day = NumericProperty ( )
"""
The day of the month to be opened by default . If not specified ,
the current number will be used .
See ` Open date dialog with the specified date < https : / / kivymd . readthedocs . io / en / latest / components / datepicker / #open-date-dialog-with-the-specified-date>`_ for more information.
: attr : ` day ` is an : class : ` ~ kivy . properties . NumericProperty `
and defaults to ` 0 ` .
"""
month = NumericProperty ( )
"""
The number of month to be opened by default . If not specified ,
the current number will be used .
See ` Open date dialog with the specified date < https : / / kivymd . readthedocs . io / en / latest / components / datepicker / #open-date-dialog-with-the-specified-date>`_ for more information.
: attr : ` month ` is an : class : ` ~ kivy . properties . NumericProperty `
and defaults to ` 0 ` .
"""
year = NumericProperty ( )
"""
The year of month to be opened by default . If not specified ,
the current number will be used .
See ` Open date dialog with the specified date < https : / / kivymd . readthedocs . io / en / latest / components / datepicker / #open-date-dialog-with-the-specified-date>`_ for more information.
: attr : ` year ` is an : class : ` ~ kivy . properties . NumericProperty `
and defaults to ` 0 ` .
"""
min_year = NumericProperty ( 1914 )
"""
The year of month to be opened by default . If not specified ,
the current number will be used .
: attr : ` min_year ` is an : class : ` ~ kivy . properties . NumericProperty `
and defaults to ` 1914 ` .
"""
max_year = NumericProperty ( 2121 )
"""
The year of month to be opened by default . If not specified ,
the current number will be used .
: attr : ` max_year ` is an : class : ` ~ kivy . properties . NumericProperty `
and defaults to ` 2121 ` .
"""
mode = OptionProperty ( " picker " , options = [ " picker " , " range " ] )
"""
Dialog type : ` ' picker ' ` type allows you to select one date ;
` ' range ' ` type allows to set a range of dates from which the
user can select a date .
Available options are : [ ` ' picker ' ` , ` ' range ' ` ] .
: attr : ` mode ` is an : class : ` ~ kivy . properties . OptionProperty `
and defaults to ` picker ` .
"""
min_date = ObjectProperty ( )
"""
The minimum value of the date range for the ` ' mode` ' parameter .
Must be an object < class ' datetime . date ' >.
See ` Open date dialog with the specified date < https : / / kivymd . readthedocs . io / en / latest / components / datepicker / #interval-date>`_ for more information.
: attr : ` min_date ` is an : class : ` ~ kivy . properties . ObjectProperty `
and defaults to ` None ` .
"""
max_date = ObjectProperty ( )
"""
The minimum value of the date range for the ` ' mode` ' parameter .
Must be an object < class ' datetime . date ' >.
See ` Open date dialog with the specified date < https : / / kivymd . readthedocs . io / en / latest / components / datepicker / #interval-date>`_ for more information.
: attr : ` max_date ` is an : class : ` ~ kivy . properties . ObjectProperty `
and defaults to ` None ` .
"""
date_range_text_error = StringProperty ( " Error date range " )
"""
Error text that will be shown on the screen in the form of a toast if the
minimum date range exceeds the maximum .
: attr : ` date_range_text_error ` is an : class : ` ~ kivy . properties . StringProperty `
and defaults to ` ' Error date range ' ` .
"""
input_field_cls = ObjectProperty ( DatePickerInputField )
"""
A class that will implement date input in the format dd / mm / yyyy .
See : class : ` ~ DatePickerInputField ` class for more information .
. . code - block : : python
class CustomInputField ( MDTextField ) :
owner = ObjectProperty ( ) # required attribute
# Required method.
def set_error ( self ) :
[ . . . ]
# Required method.
def get_list_date ( self ) :
[ . . . ]
# Required method.
def input_filter ( self ) :
[ . . . ]
def show_date_picker ( self ) :
date_dialog = MDDatePicker ( input_field_cls = CustomInputField )
: attr : ` input_field_cls ` is an : class : ` ~ kivy . properties . ObjectProperty `
and defaults to : class : ` ~ DatePickerInputField ` .
"""
sel_year = NumericProperty ( )
sel_month = NumericProperty ( )
sel_day = NumericProperty ( )
_calendar_layout = ObjectProperty ( )
_calendar_list = None
_enter_data_field = None
_enter_data_field_two = None
_enter_data_field_container = None
_date_range = [ ]
_scale_calendar_layout = NumericProperty ( 1 )
_scale_year_layout = NumericProperty ( 0 )
_shift_dialog_height = NumericProperty ( 0 )
_input_date_dialog_open = BooleanProperty ( False )
_select_year_dialog_open = False
_start_range_date = 0
_end_range_date = 0
def __init__ (
self ,
year = None ,
month = None ,
day = None ,
firstweekday = 0 ,
* * kwargs ,
) :
self . today = date . today ( )
self . calendar = calendar . Calendar ( firstweekday )
self . sel_year = year if year else self . today . year
self . sel_month = month if month else self . today . month
self . sel_day = day if day else self . today . day
self . month = self . sel_month
self . year = self . sel_year
self . day = self . sel_day
super ( ) . __init__ ( * * kwargs )
self . theme_cls . bind ( device_orientation = self . on_device_orientation )
if self . max_date and self . min_date :
if self . min_date and not isinstance ( self . min_date , date ) :
raise DatePickerTypeDateError (
" ' min_date ' must be of class <class ' datetime.date ' > "
)
if self . max_date and not isinstance ( self . max_date , date ) :
raise DatePickerTypeDateError (
" ' max_date ' must be of class <class ' datetime.date ' > "
)
self . compare_date_range ( )
self . _date_range = self . get_date_range ( )
self . generate_list_widgets_days ( )
self . update_calendar ( self . sel_year , self . sel_month )
def on_device_orientation (
self , instance_theme_manager : ThemeManager , orientation_value : str
) - > None :
""" Called when the device ' s screen orientation changes. """
if self . _input_date_dialog_open :
if orientation_value == " portrait " :
self . _shift_dialog_height = dp ( 250 )
if orientation_value == " landscape " :
self . _shift_dialog_height = dp ( 138 )
def on_ok_button_pressed ( self ) - > None :
"""
Called when the ' OK ' button is pressed to confirm the date entered .
"""
if self . _enter_data_field and not self . is_date_valaid (
self . _enter_data_field . text
) :
self . _enter_data_field . set_error ( )
return
if self . _enter_data_field_two and not self . is_date_valaid (
self . _enter_data_field_two . text
) :
self . _enter_data_field_two . set_error ( )
return
self . dispatch (
" on_save " ,
date ( self . sel_year , self . sel_month , self . sel_day ) ,
self . _date_range ,
)
def is_date_valaid ( self , date : str ) - > bool :
""" Checks the valid of the currently entered date. """
try :
time . strptime ( date , " %d / % m/ % Y " )
return True
except ValueError :
return False
def transformation_from_dialog_select_year ( self ) - > None :
self . ids . chevron_left . disabled = False
self . ids . chevron_right . disabled = False
self . ids . _year_layout . disabled = True
self . ids . triangle . disabled = False
self . _select_year_dialog_open = False
self . ids . triangle . icon = " menu-down "
Animation ( opacity = 1 , d = 0.15 ) . start ( self . ids . chevron_left )
Animation ( opacity = 1 , d = 0.15 ) . start ( self . ids . chevron_right )
Animation ( _scale_year_layout = 0 , d = 0.15 ) . start ( self )
2022-10-08 17:17:59 +02:00
Animation ( _scale_calendar_layout = 1 , d = 0.15 ) . start ( self )
2022-07-07 22:16:10 +02:00
2022-10-08 17:17:59 +02:00
# Move selection to the same day and month of the selected year.
self . sel_year = self . year
last_day = calendar . monthrange ( self . year , self . sel_month ) [ 1 ]
self . sel_day = min ( self . sel_day , last_day )
2022-07-07 22:16:10 +02:00
self . update_calendar ( self . year , self . month )
def transformation_to_dialog_select_year ( self ) - > None :
def disabled_chevron_buttons ( * args ) :
self . ids . chevron_left . disabled = True
self . ids . chevron_right . disabled = True
self . _select_year_dialog_open = True
self . ids . _year_layout . disabled = False
Animation ( opacity = 0 , d = 0.15 ) . start ( self . ids . chevron_left )
Animation ( opacity = 0 , d = 0.15 ) . start ( self . ids . chevron_right )
2022-10-08 17:17:59 +02:00
Animation ( _scale_calendar_layout = 0 , d = 0.15 ) . start ( self )
2022-07-07 22:16:10 +02:00
anim = Animation ( _scale_year_layout = 1 , d = 0.15 )
anim . bind ( on_complete = disabled_chevron_buttons )
anim . start ( self )
self . ids . triangle . icon = " menu-up "
self . generate_list_widgets_years ( )
self . set_position_to_current_year ( )
2022-10-08 17:17:59 +02:00
if self . min_year < = self . year < self . max_year :
index = self . year - self . min_year
self . ids . _year_layout . children [ 0 ] . select_node ( index )
else :
self . ids . _year_layout . children [ 0 ] . clear_selection ( )
2022-07-07 22:16:10 +02:00
def transformation_to_dialog_input_date ( self ) - > None :
def set_date_to_input_field ( ) :
if not self . _enter_data_field_two :
# Date of current day.
self . _enter_data_field . text = (
f " { ' ' if self . sel_day > = 10 else ' 0 ' } "
f " { self . sel_day } / "
f " { ' ' if self . sel_month > = 10 else ' 0 ' } "
f " { self . sel_month } / { self . sel_year } "
)
else :
# Range start date.
self . _enter_data_field . text = (
f " { ' ' if self . min_date . day > = 10 else ' 0 ' } "
f " { self . min_date . day } / "
f " { ' ' if self . min_date . month > = 10 else ' 0 ' } "
f " { self . min_date . month } / { self . min_date . year } "
)
def set_date_to_input_field_two ( ) - > None :
# Range end date.
self . _enter_data_field_two . text = (
f " { ' ' if self . max_date . day > = 10 else ' 0 ' } "
f " { self . max_date . day } / "
f " { ' ' if self . max_date . month > = 10 else ' 0 ' } "
f " { self . max_date . month } / { self . max_date . year } "
)
self . ids . triangle . disabled = True
if self . _select_year_dialog_open :
self . transformation_from_dialog_select_year ( )
self . _input_date_dialog_open = True
self . _enter_data_field_container = DatePickerInputFieldContainer (
owner = self
)
self . _enter_data_field = self . get_field ( )
if self . min_date and self . max_date :
self . _enter_data_field_two = self . get_field ( )
set_date_to_input_field_two ( )
set_date_to_input_field ( )
self . _enter_data_field_container . add_widget ( self . _enter_data_field )
if self . _enter_data_field_two :
self . _enter_data_field_container . add_widget (
self . _enter_data_field_two
)
self . ids . container . add_widget ( self . _enter_data_field_container )
self . ids . edit_icon . icon = " calendar "
self . ids . label_title . text = self . title_input
Animation (
_shift_dialog_height = dp ( 250 )
if self . theme_cls . device_orientation == " portrait "
else dp ( 138 ) ,
_scale_calendar_layout = 0 ,
d = 0.15 ,
) . start ( self )
Animation (
opacity = 0 ,
d = 0.15 if self . theme_cls . device_orientation == " portrait " else 0 ,
) . start ( self . ids . chevron_left )
Animation (
opacity = 0 ,
d = 0.15 if self . theme_cls . device_orientation == " portrait " else 0 ,
) . start ( self . ids . chevron_right )
Animation ( opacity = 0 , d = 0.15 ) . start ( self . ids . label_month_selector )
Animation ( opacity = 0 , d = 0.15 ) . start ( self . ids . triangle )
Animation ( opacity = 1 , d = 0.15 ) . start ( self . _enter_data_field )
if self . _enter_data_field_two :
Animation ( opacity = 1 , d = 0.15 ) . start ( self . _enter_data_field_two )
self . ids . label_full_date . text = self . set_text_full_date (
self . sel_year ,
self . sel_month ,
self . sel_day ,
self . theme_cls . device_orientation ,
)
def transformation_from_dialog_input_date (
self , interval : Union [ int , float ]
) - > None :
self . _input_date_dialog_open = False
self . ids . label_full_date . text = self . set_text_full_date (
self . sel_year ,
self . sel_month ,
self . sel_day ,
self . theme_cls . device_orientation ,
)
self . ids . triangle . disabled = False
self . ids . container . remove_widget ( self . _enter_data_field_container )
Animation (
_shift_dialog_height = dp ( 0 ) , _scale_calendar_layout = 1 , d = 0.15
) . start ( self )
Animation (
opacity = 1 ,
d = 0.15 if self . theme_cls . device_orientation == " portrait " else 0.65 ,
) . start ( self . ids . chevron_left )
Animation (
opacity = 1 ,
d = 0.15 if self . theme_cls . device_orientation == " portrait " else 0.65 ,
) . start ( self . ids . chevron_right )
Animation ( opacity = 1 , d = 0.15 ) . start ( self . ids . label_month_selector )
Animation ( opacity = 1 , d = 0.15 ) . start ( self . ids . triangle )
Animation ( opacity = 0 , d = 0.15 ) . start ( self . _enter_data_field )
self . ids . edit_icon . icon = " pencil "
self . ids . label_title . text = self . title
if not self . min_date and not self . max_date :
list_date = self . _enter_data_field . get_list_date ( )
if len ( list_date ) == 3 and len ( list_date [ 2 ] ) == 4 :
2022-10-08 17:17:59 +02:00
self . sel_day = int ( list_date [ 0 ] )
self . sel_month = int ( list_date [ 1 ] )
self . sel_year = int ( list_date [ 2 ] )
self . update_calendar ( self . sel_year , self . sel_month )
2022-07-07 22:16:10 +02:00
elif self . min_date and self . max_date :
list_min_date = self . _enter_data_field . get_list_date ( )
list_max_date = self . _enter_data_field_two . get_list_date ( )
if len ( list_min_date ) == 3 and len ( list_min_date [ 2 ] ) == 4 :
self . min_date = date (
int ( list_min_date [ 2 ] ) ,
int ( list_min_date [ 1 ] ) ,
int ( list_min_date [ 0 ] ) ,
)
if len ( list_max_date ) == 3 and len ( list_max_date [ 2 ] ) == 4 :
self . max_date = date (
int ( list_max_date [ 2 ] ) ,
int ( list_max_date [ 1 ] ) ,
int ( list_max_date [ 0 ] ) ,
)
self . update_calendar_for_date_range ( )
self . ids . label_full_date . text = self . set_text_full_date (
int ( list_max_date [ 2 ] ) ,
int ( list_max_date [ 1 ] ) ,
int ( list_max_date [ 0 ] ) ,
self . theme_cls . device_orientation ,
)
def compare_date_range ( self ) - > None :
# TODO: Add behavior if the minimum date range exceeds the maximum
# date range. Use toast?
if self . max_date < = self . min_date :
raise DatePickerTypeDateError (
" `max_date` value cannot be less than or equal "
" to ' min_date ' value "
)
def update_calendar_for_date_range ( self ) - > None :
# self.compare_date_range()
self . _date_range = self . get_date_range ( )
self . update_calendar ( self . year , self . month )
def update_text_full_date ( self , list_date ) - > None :
"""
Updates the title of the week , month and number day name
in an open date input dialog .
"""
if len ( list_date ) == 1 and len ( list_date [ 0 ] ) == 2 :
self . ids . label_full_date . text = self . set_text_full_date (
self . sel_year ,
self . sel_month ,
list_date [ 0 ] ,
self . theme_cls . device_orientation ,
)
if len ( list_date ) == 2 and len ( list_date [ 1 ] ) == 2 :
self . ids . label_full_date . text = self . set_text_full_date (
self . sel_year ,
int ( list_date [ 1 ] ) ,
int ( list_date [ 0 ] ) ,
self . theme_cls . device_orientation ,
)
if len ( list_date ) == 3 and len ( list_date [ 2 ] ) == 4 :
self . ids . label_full_date . text = self . set_text_full_date (
int ( list_date [ 2 ] ) ,
int ( list_date [ 1 ] ) ,
int ( list_date [ 0 ] ) ,
self . theme_cls . device_orientation ,
)
def update_calendar ( self , year , month ) - > None :
2022-10-08 17:17:59 +02:00
self . year , self . month = year , month
if self . mode == " picker " :
selected_date = date ( self . sel_year , self . sel_month , self . sel_day )
selected_dates = { selected_date }
2022-07-07 22:16:10 +02:00
else :
2022-10-08 17:17:59 +02:00
selected_dates = { self . _start_range_date , self . _end_range_date }
dates = self . calendar . itermonthdates ( year , month )
for widget , widget_date in zip_longest ( self . _calendar_list , dates ) :
# Only widgets whose dates are in the displayed month are visible.
visible = (
widget_date is not None
and widget_date . month == month
and widget_date . year == year
)
widget . text = str ( widget_date . day ) if visible else " "
widget . current_year = year
widget . current_month = month
widget . is_today = visible and widget_date == self . today
widget . is_selected = visible and widget_date in selected_dates
# I don't understand why, but this line is important. Without this
# line, some widgets that we are trying to disable remain enabled.
widget . disabled = False
widget . disabled = (
not visible
or self . mode == " range "
and self . _date_range
and widget_date not in self . _date_range
)
2022-07-07 22:16:10 +02:00
def get_field ( self ) - > MDTextField :
""" Creates and returns a text field object used to enter dates. """
if issubclass ( self . input_field_cls , MDTextField ) :
2022-10-08 17:17:59 +02:00
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
)
2022-07-07 22:16:10 +02:00
field = self . input_field_cls (
owner = self ,
helper_text = self . helper_text ,
2022-10-08 17:17:59 +02:00
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 ,
2022-07-07 22:16:10 +02:00
)
return field
else :
raise TypeError (
" The `input_field_cls` parameter must be an object of the "
" `kivymd.uix.textfield.MDTextField class` "
)
def get_date_range ( self ) - > list :
date_range = [
self . min_date + datetime . timedelta ( days = x )
for x in range ( ( self . max_date - self . min_date ) . days + 1 )
]
return date_range
def set_text_full_date ( self , year , month , day , orientation ) :
"""
Returns a string of type " Tue, Feb 2 " or " Tue, \n Feb 2 " for a date
choose and a string like " Feb 15 - Mar 23 " or " Feb 15, \n Mar 23 " for
a date range .
"""
if 12 < int ( month ) < 0 :
raise ValueError (
" set_text_full_date: \n \t " f " Month [ { month } ] out of range. "
)
if int ( day ) > calendar . monthrange ( int ( year ) , ( month ) ) [ 1 ] :
2022-10-08 17:17:59 +02:00
return " "
2022-07-07 22:16:10 +02:00
date = datetime . date ( int ( year ) , int ( month ) , int ( day ) )
separator = (
" \n "
if ( orientation == " landscape " and not self . _input_date_dialog_open )
else " "
)
if self . mode == " picker " :
if not self . min_date and not self . max_date :
return (
date . strftime ( " %a , " ) . capitalize ( )
+ separator
+ date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
)
else :
return (
self . min_date . strftime ( " % b " ) . capitalize ( )
+ str ( self . min_date . day ) . lstrip ( " 0 " )
+ (
" - "
if orientation == " portrait "
else (
" , \n " if not self . _input_date_dialog_open else " , "
)
)
+ self . max_date . strftime ( " % b " ) . capitalize ( )
+ str ( self . max_date . day ) . lstrip ( " 0 " )
)
elif self . mode == " range " :
if self . _start_range_date and self . _end_range_date :
if (
orientation == " landscape "
and " - " in self . ids . label_full_date . text
) :
return (
self . ids . label_full_date . text . split ( " - " ) [ 0 ] . strip ( )
+ ( " , \n " if not self . _input_date_dialog_open else " - " )
+ date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
)
else :
if (
orientation == " landscape "
and " , " in self . ids . label_full_date . text
) :
return (
self . ids . label_full_date . text . split ( " , " ) [ 0 ] . strip ( )
+ (
" , \n "
if not self . _input_date_dialog_open
else " - "
)
+ date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
)
if (
orientation == " portrait "
and " , " in self . ids . label_full_date . text
) :
return (
self . ids . label_full_date . text . split ( " , " ) [ 0 ] . strip ( )
+ " - "
+ date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
)
if (
orientation == " portrait "
and " - " in self . ids . label_full_date . text
) :
return (
self . ids . label_full_date . text . split ( " - " ) [ 0 ] . strip ( )
+ " - "
+ date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
)
elif self . _start_range_date and not self . _end_range_date :
return (
(
date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
+ " - End "
)
if orientation != " landscape "
else (
date . strftime ( " % b " ) . capitalize ( )
+ str ( day ) . lstrip ( " 0 " )
+ " {} End " . format (
" , \n " if not self . _input_date_dialog_open else " - "
)
)
)
elif not self . _start_range_date and not self . _end_range_date :
return (
" Start - End "
if orientation != " landscape "
else " Start {} End " . format (
" , \n " if not self . _input_date_dialog_open else " - "
)
)
def set_selected_widget ( self , widget ) - > None :
2022-10-08 17:17:59 +02:00
self . sel_year = self . year
self . sel_month = self . month
2022-07-07 22:16:10 +02:00
self . sel_day = int ( widget . text )
2022-10-08 17:17:59 +02:00
self . update_calendar ( self . sel_year , self . sel_month )
2022-07-07 22:16:10 +02:00
def set_month_day ( self , day ) - > None :
2022-10-08 17:17:59 +02:00
# This method is no longer used. The code bellow repeats the behavior
# that was previously required of it for backward compatibility
# reasons.
self . sel_day = day
self . update_calendar ( self . sel_year , self . sel_month )
2022-07-07 22:16:10 +02:00
def set_position_to_current_year ( self ) - > None :
2022-10-08 17:17:59 +02:00
year_layout = self . ids . _year_layout
# When this method is called for the first time, RecycleView has not
# yet added widgets to the year list, so we use the default height.
widget_height = year_layout . children [ 0 ] . default_size [ 1 ]
cols_amount = year_layout . children [ 0 ] . cols
rows_amount = math . ceil ( ( self . max_year - self . min_year ) / cols_amount )
row_index = ( self . year - self . min_year ) / / cols_amount
# To find the middle of the current year widget, we add the height of
# the rows under this widget with half the widget height.
widget_center_y = ( rows_amount - row_index - 1 + 0.5 ) * widget_height
viewport_height = year_layout . height
year_list_height = rows_amount * widget_height
# If there are too few years in the list to fill the entire viewport,
# RecycleView displays additional empty space outside the list.
# We have to move the viewport up so that this space is displayed
# under the years list. Also, this guard condition protects against
# the division by zero error below.
if viewport_height > = year_list_height :
year_layout . scroll_y = 1
return
viewport_bottom = widget_center_y - 0.5 * viewport_height
# We set scroll_y property to the ratio of the actual lifting height
# of the viewport to the maximum possible, and clamp this ratio in the
# range from 0 to 1 so that the viewport still is in a valid position
# if it is impossible to show the widget in the middle.
scroll_y = viewport_bottom / ( year_list_height - viewport_height )
year_layout . scroll_y = min ( 1 , max ( 0 , scroll_y ) )
2022-07-07 22:16:10 +02:00
def generate_list_widgets_years ( self ) - > None :
2022-10-08 17:17:59 +02:00
self . ids . _year_layout . data = [ ]
2022-07-07 22:16:10 +02:00
for i , number_year in enumerate ( range ( self . min_year , self . max_year ) ) :
self . ids . _year_layout . data . append (
{
" owner " : self ,
" text " : str ( number_year ) ,
" index " : i ,
" viewclass " : " DatePickerYearSelectableItem " ,
}
)
def generate_list_widgets_days ( self ) - > None :
calendar_list = [ ]
for day in self . calendar . iterweekdays ( ) :
weekday_label = DatePickerWeekdayLabel (
text = calendar . day_name [ day ] [ 0 ] . upper ( ) ,
owner = self ,
hint_text = calendar . day_name [ day ] ,
)
weekday_label . font_name = self . font_name
self . _calendar_layout . add_widget ( weekday_label )
for i , j in enumerate ( range ( 6 * 7 ) ) : # 6 weeks, 7 days a week
day_selectable_item = DatePickerDaySelectableItem (
index = i ,
owner = self ,
current_month = int ( self . month ) ,
current_year = int ( self . year ) ,
)
calendar_list . append ( day_selectable_item )
self . _calendar_layout . add_widget ( day_selectable_item )
self . _calendar_list = calendar_list
def change_month ( self , operation : str ) - > None :
"""
Called when " chevron-left " and " chevron-right " buttons are pressed .
Switches the calendar to the previous / next month .
"""
2022-10-08 17:17:59 +02:00
month_delta = 1 if operation == " next " else - 1
year = self . year + ( self . month - 1 + month_delta ) / / 12
month = ( self . month - 1 + month_delta ) % 12 + 1
if year < = 0 :
year , month = 1 , 1
2022-07-07 22:16:10 +02:00
self . update_calendar ( year , month )