bugfix
This commit is contained in:
parent
1cf4c0a421
commit
8657c9764f
@ -0,0 +1,9 @@
|
|||||||
|
import os
|
||||||
|
from kivy import logger
|
||||||
|
__version_info__ = (0.5.0)
|
||||||
|
__version__ = '0.5.0'
|
||||||
|
|
||||||
|
path = os.path.dirname(__file__)
|
||||||
|
fonts_path = os.path.join(path,"ttf/")
|
||||||
|
images_path = os.path.join(path,'imgs/')
|
||||||
|
logger.info("kivyblocks:kivblocks version:{}".format(__version__)
|
@ -55,6 +55,7 @@ from .widgetExt.inputext import FloatInput,IntegerInput, \
|
|||||||
StrInput,SelectInput, BoolInput, AmountInput
|
StrInput,SelectInput, BoolInput, AmountInput
|
||||||
from .widgetExt.messager import Messager
|
from .widgetExt.messager import Messager
|
||||||
from .charts.bar import Bar
|
from .charts.bar import Bar
|
||||||
|
from .tree import Tree, TextTree
|
||||||
|
|
||||||
if platform == 'android':
|
if platform == 'android':
|
||||||
from .widgetExt.phonebutton import PhoneButton
|
from .widgetExt.phonebutton import PhoneButton
|
||||||
@ -62,7 +63,11 @@ if platform == 'android':
|
|||||||
|
|
||||||
class PressableImage(ButtonBehavior,AsyncImage):
|
class PressableImage(ButtonBehavior,AsyncImage):
|
||||||
def on_press(self):
|
def on_press(self):
|
||||||
print
|
pass
|
||||||
|
|
||||||
|
class PressableLabel(ButtonBehavior, Label):
|
||||||
|
def on_press(self):
|
||||||
|
pass
|
||||||
|
|
||||||
class Text(Label):
|
class Text(Label):
|
||||||
bgColor = ListProperty([0.5,0.5,0.5,1])
|
bgColor = ListProperty([0.5,0.5,0.5,1])
|
||||||
|
@ -28,6 +28,7 @@ from .utils import *
|
|||||||
from .pagescontainer import PageContainer
|
from .pagescontainer import PageContainer
|
||||||
from .widgetExt.messager import Messager
|
from .widgetExt.messager import Messager
|
||||||
from .blocks import Blocks
|
from .blocks import Blocks
|
||||||
|
from .theming import ThemeManager
|
||||||
from appPublic.rsa import RSA
|
from appPublic.rsa import RSA
|
||||||
|
|
||||||
class ServerInfo:
|
class ServerInfo:
|
||||||
@ -68,6 +69,7 @@ class BlocksApp(App):
|
|||||||
def build(self):
|
def build(self):
|
||||||
config = getConfig()
|
config = getConfig()
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.theme_cls = ThemeManager()
|
||||||
self.serverinfo = ServerInfo()
|
self.serverinfo = ServerInfo()
|
||||||
self.title = 'Test Title'
|
self.title = 'Test Title'
|
||||||
self.blocks = Blocks()
|
self.blocks = Blocks()
|
||||||
|
676
kivyblocks/color_definitions.py
Normal file
676
kivyblocks/color_definitions.py
Normal file
@ -0,0 +1,676 @@
|
|||||||
|
colors = {
|
||||||
|
'Red': {
|
||||||
|
'200': 'ef9a9a',
|
||||||
|
'900': 'b71c1c',
|
||||||
|
'600': 'e53935',
|
||||||
|
'A100': 'ff8a80',
|
||||||
|
'300': 'e57373',
|
||||||
|
'A400': 'ff1744',
|
||||||
|
'700': 'd32f2f',
|
||||||
|
'50': 'ffebee',
|
||||||
|
'A700': 'd50000',
|
||||||
|
'400': 'ef5350',
|
||||||
|
'100': 'ffcdd2',
|
||||||
|
'800': 'c62828',
|
||||||
|
'A200': 'ff5252',
|
||||||
|
'500': 'f44336'
|
||||||
|
},
|
||||||
|
'Pink': {
|
||||||
|
'50': 'fce4ec',
|
||||||
|
'100': 'f8bbd0',
|
||||||
|
'200': 'f48fb1',
|
||||||
|
'300': 'f06292',
|
||||||
|
'400': 'ec407a',
|
||||||
|
'500': 'e91e63',
|
||||||
|
'600': 'd81b60',
|
||||||
|
'700': 'C2185B',
|
||||||
|
'800': 'ad1457',
|
||||||
|
'900': '88e4ff',
|
||||||
|
'A100': 'ff80ab',
|
||||||
|
'A400': 'F50057',
|
||||||
|
'A700': 'c51162',
|
||||||
|
'A200': 'ff4081'
|
||||||
|
},
|
||||||
|
'Purple': {
|
||||||
|
'200': 'ce93d8',
|
||||||
|
'900': '4a148c',
|
||||||
|
'600': '8e24aa',
|
||||||
|
'A100': 'ea80fc',
|
||||||
|
'300': 'ba68c8',
|
||||||
|
'A400': 'D500F9',
|
||||||
|
'700': '7b1fa2',
|
||||||
|
'50': 'f3e5f5',
|
||||||
|
'A700': 'AA00FF',
|
||||||
|
'400': 'ab47bc',
|
||||||
|
'100': 'e1bee7',
|
||||||
|
'800': '6a1b9a',
|
||||||
|
'A200': 'e040fb',
|
||||||
|
'500': '9c27b0'
|
||||||
|
},
|
||||||
|
'DeepPurple': {
|
||||||
|
'200': 'b39ddb',
|
||||||
|
'900': '311b92',
|
||||||
|
'600': '5e35b1',
|
||||||
|
'A100': 'b388ff',
|
||||||
|
'300': '9575cd',
|
||||||
|
'A400': '651fff',
|
||||||
|
'700': '512da8',
|
||||||
|
'50': 'ede7f6',
|
||||||
|
'A700': '6200EA',
|
||||||
|
'400': '7e57c2',
|
||||||
|
'100': 'd1c4e9',
|
||||||
|
'800': '4527a0',
|
||||||
|
'A200': '7c4dff',
|
||||||
|
'500': '673ab7'
|
||||||
|
},
|
||||||
|
'Indigo': {
|
||||||
|
'200': '9fa8da',
|
||||||
|
'900': '1a237e',
|
||||||
|
'600': '3949ab',
|
||||||
|
'A100': '8c9eff',
|
||||||
|
'300': '7986cb',
|
||||||
|
'A400': '3d5afe',
|
||||||
|
'700': '303f9f',
|
||||||
|
'50': 'e8eaf6',
|
||||||
|
'A700': '304ffe',
|
||||||
|
'400': '5c6bc0',
|
||||||
|
'100': 'c5cae9',
|
||||||
|
'800': '283593',
|
||||||
|
'A200': '536dfe',
|
||||||
|
'500': '3f51b5'
|
||||||
|
},
|
||||||
|
'Blue': {
|
||||||
|
'200': '90caf9',
|
||||||
|
'900': '0D47A1',
|
||||||
|
'600': '1e88e5',
|
||||||
|
'A100': '82b1ff',
|
||||||
|
'300': '64b5f6',
|
||||||
|
'A400': '2979ff',
|
||||||
|
'700': '1976d2',
|
||||||
|
'50': 'e3f2fd',
|
||||||
|
'A700': '2962ff',
|
||||||
|
'400': '42a5f5',
|
||||||
|
'100': 'bbdefb',
|
||||||
|
'800': '1565c0',
|
||||||
|
'A200': '448aff',
|
||||||
|
'500': '2196f3'
|
||||||
|
},
|
||||||
|
'LightBlue': {
|
||||||
|
'200': '81d4fa',
|
||||||
|
'900': '01579B',
|
||||||
|
'600': '039BE5',
|
||||||
|
'A100': '80d8ff',
|
||||||
|
'300': '4fc3f7',
|
||||||
|
'A400': '00B0FF',
|
||||||
|
'700': '0288D1',
|
||||||
|
'50': 'e1f5fe',
|
||||||
|
'A700': '0091EA',
|
||||||
|
'400': '29b6f6',
|
||||||
|
'100': 'b3e5fc',
|
||||||
|
'800': '0277BD',
|
||||||
|
'A200': '40c4ff',
|
||||||
|
'500': '03A9F4'
|
||||||
|
},
|
||||||
|
'Cyan': {
|
||||||
|
'200': '80deea',
|
||||||
|
'900': '006064',
|
||||||
|
'600': '00ACC1',
|
||||||
|
'A100': '84ffff',
|
||||||
|
'300': '4dd0e1',
|
||||||
|
'A400': '00E5FF',
|
||||||
|
'700': '0097A7',
|
||||||
|
'50': 'e0f7fa',
|
||||||
|
'A700': '00B8D4',
|
||||||
|
'400': '26c6da',
|
||||||
|
'100': 'b2ebf2',
|
||||||
|
'800': '00838F',
|
||||||
|
'A200': '18ffff',
|
||||||
|
'500': '00BCD4'
|
||||||
|
},
|
||||||
|
'Teal': {
|
||||||
|
'200': '80cbc4',
|
||||||
|
'900': '004D40',
|
||||||
|
'600': '00897B',
|
||||||
|
'A100': 'a7ffeb',
|
||||||
|
'300': '4db6ac',
|
||||||
|
'A400': '1de9b6',
|
||||||
|
'700': '00796B',
|
||||||
|
'50': 'e0f2f1',
|
||||||
|
'A700': '00BFA5',
|
||||||
|
'400': '26a69a',
|
||||||
|
'100': 'b2dfdb',
|
||||||
|
'800': '00695C',
|
||||||
|
'A200': '64ffda',
|
||||||
|
'500': '009688'
|
||||||
|
},
|
||||||
|
'Green': {
|
||||||
|
'200': 'a5d6a7',
|
||||||
|
'900': '1b5e20',
|
||||||
|
'600': '43a047',
|
||||||
|
'A100': 'b9f6ca',
|
||||||
|
'300': '81c784',
|
||||||
|
'A400': '00E676',
|
||||||
|
'700': '388e3c',
|
||||||
|
'50': 'e8f5e9',
|
||||||
|
'A700': '00C853',
|
||||||
|
'400': '66bb6a',
|
||||||
|
'100': 'c8e6c9',
|
||||||
|
'800': '2e7d32',
|
||||||
|
'A200': '69f0ae',
|
||||||
|
'500': '4caf50'
|
||||||
|
},
|
||||||
|
'LightGreen': {
|
||||||
|
'200': 'c5e1a5',
|
||||||
|
'900': '33691e',
|
||||||
|
'600': '7cb342',
|
||||||
|
'A100': 'ccff90',
|
||||||
|
'300': 'aed581',
|
||||||
|
'A400': '76FF03',
|
||||||
|
'700': '689f38',
|
||||||
|
'50': 'f1f8e9',
|
||||||
|
'A700': '64dd17',
|
||||||
|
'400': '9ccc65',
|
||||||
|
'100': 'dcedc8',
|
||||||
|
'800': '558b2f',
|
||||||
|
'A200': 'b2ff59',
|
||||||
|
'500': '8bc34a'
|
||||||
|
},
|
||||||
|
'Lime': {
|
||||||
|
'200': 'e6ee9c',
|
||||||
|
'900': '827717',
|
||||||
|
'600': 'c0ca33',
|
||||||
|
'A100': 'f4ff81',
|
||||||
|
'300': 'dce775',
|
||||||
|
'A400': 'C6FF00',
|
||||||
|
'700': 'afb42b',
|
||||||
|
'50': 'f9fbe7',
|
||||||
|
'A700': 'AEEA00',
|
||||||
|
'400': 'd4e157',
|
||||||
|
'100': 'f0f4c3',
|
||||||
|
'800': '9e9d24',
|
||||||
|
'A200': 'eeff41',
|
||||||
|
'500': 'cddc39'
|
||||||
|
},
|
||||||
|
'Yellow': {
|
||||||
|
'200': 'fff59d',
|
||||||
|
'900': 'f57f17',
|
||||||
|
'600': 'fdd835',
|
||||||
|
'A100': 'ffff8d',
|
||||||
|
'300': 'fff176',
|
||||||
|
'A400': 'FFEA00',
|
||||||
|
'700': 'fbc02d',
|
||||||
|
'50': 'fffde7',
|
||||||
|
'A700': 'FFD600',
|
||||||
|
'400': 'ffee58',
|
||||||
|
'100': 'fff9c4',
|
||||||
|
'800': 'f9a825',
|
||||||
|
'A200': 'FFFF00',
|
||||||
|
'500': 'ffeb3b'
|
||||||
|
},
|
||||||
|
'Amber': {
|
||||||
|
'200': 'ffe082',
|
||||||
|
'900': 'FF6F00',
|
||||||
|
'600': 'FFB300',
|
||||||
|
'A100': 'ffe57f',
|
||||||
|
'300': 'ffd54f',
|
||||||
|
'A400': 'FFC400',
|
||||||
|
'700': 'FFA000',
|
||||||
|
'50': 'fff8e1',
|
||||||
|
'A700': 'FFAB00',
|
||||||
|
'400': 'ffca28',
|
||||||
|
'100': 'ffecb3',
|
||||||
|
'800': 'FF8F00',
|
||||||
|
'A200': 'ffd740',
|
||||||
|
'500': 'FFC107'
|
||||||
|
},
|
||||||
|
'Orange': {
|
||||||
|
'200': 'ffcc80',
|
||||||
|
'900': 'E65100',
|
||||||
|
'600': 'FB8C00',
|
||||||
|
'A100': 'ffd180',
|
||||||
|
'300': 'ffb74d',
|
||||||
|
'A400': 'FF9100',
|
||||||
|
'700': 'F57C00',
|
||||||
|
'50': 'fff3e0',
|
||||||
|
'A700': 'FF6D00',
|
||||||
|
'400': 'ffa726',
|
||||||
|
'100': 'ffe0b2',
|
||||||
|
'800': 'EF6C00',
|
||||||
|
'A200': 'ffab40',
|
||||||
|
'500': 'FF9800'
|
||||||
|
},
|
||||||
|
'DeepOrange': {
|
||||||
|
'200': 'ffab91',
|
||||||
|
'900': 'bf36c',
|
||||||
|
'600': 'f4511e',
|
||||||
|
'A100': 'ff9e80',
|
||||||
|
'300': 'ff8a65',
|
||||||
|
'A400': 'FF3D00',
|
||||||
|
'700': 'e64a19',
|
||||||
|
'50': 'fbe9e7',
|
||||||
|
'A700': 'DD2C00',
|
||||||
|
'400': 'ff7043',
|
||||||
|
'100': 'ffccbc',
|
||||||
|
'800': 'd84315',
|
||||||
|
'A200': 'ff6e40',
|
||||||
|
'500': 'ff5722'
|
||||||
|
},
|
||||||
|
'Brown': {
|
||||||
|
'200': 'bcaaa4',
|
||||||
|
'900': '3e2723',
|
||||||
|
'600': '6d4c41',
|
||||||
|
'300': 'a1887f',
|
||||||
|
'700': '5d4037',
|
||||||
|
'50': 'efebe9',
|
||||||
|
'400': '8d6e63',
|
||||||
|
'100': 'd7ccc8',
|
||||||
|
'800': '4e342e',
|
||||||
|
'500': '795548'
|
||||||
|
},
|
||||||
|
'Grey': {
|
||||||
|
'200': 'eeeeee',
|
||||||
|
'900': '212121',
|
||||||
|
'600': '757575',
|
||||||
|
'300': 'e0e0e0',
|
||||||
|
'700': '616161',
|
||||||
|
'50': 'fafafa',
|
||||||
|
'400': 'bdbdbd',
|
||||||
|
'100': 'f5f5f5',
|
||||||
|
'800': '424242',
|
||||||
|
'500': '9e9e9e'
|
||||||
|
},
|
||||||
|
'BlueGrey': {
|
||||||
|
'200': 'b0bec5',
|
||||||
|
'900': '263238',
|
||||||
|
'600': '546e7a',
|
||||||
|
'300': '90a4ae',
|
||||||
|
'700': '455a64',
|
||||||
|
'50': 'eceff1',
|
||||||
|
'400': '78909c',
|
||||||
|
'100': 'cfd8dc',
|
||||||
|
'800': '37474f',
|
||||||
|
'500': '607d8b'
|
||||||
|
},
|
||||||
|
|
||||||
|
'Light': {
|
||||||
|
'StatusBar': 'E0E0E0',
|
||||||
|
'AppBar': 'F5F5F5',
|
||||||
|
'Background': 'FAFAFA',
|
||||||
|
'CardsDialogs': 'FFFFFF',
|
||||||
|
'FlatButtonDown': 'cccccc'
|
||||||
|
},
|
||||||
|
|
||||||
|
'Dark': {
|
||||||
|
'StatusBar': '000000',
|
||||||
|
'AppBar': '212121',
|
||||||
|
'Background': '303030',
|
||||||
|
'CardsDialogs': '424242',
|
||||||
|
'FlatButtonDown': '999999'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
light_colors = {
|
||||||
|
'Red': ['50', '100', '200', '300', 'A100'],
|
||||||
|
'Pink': ['50', '100', '200', 'A100'],
|
||||||
|
'Purple': ['50', '100', '200', 'A100'],
|
||||||
|
'DeepPurple': ['50', '100', '200', 'A100'],
|
||||||
|
'Indigo': ['50', '100', '200', 'A100'],
|
||||||
|
'Blue': ['50','100', '200', '300', '400', 'A100'],
|
||||||
|
'LightBlue': ['50', '100', '200', '300', '400', '500', 'A100', 'A200',
|
||||||
|
'A400'],
|
||||||
|
'Cyan': ['50', '100', '200', '300', '400', '500', '600', 'A100', 'A200',
|
||||||
|
'A400', 'A700'],
|
||||||
|
'Teal': ['50', '100', '200', '300', '400', 'A100', 'A200', 'A400', 'A700'],
|
||||||
|
'Green': ['50', '100', '200', '300', '400', '500', 'A100', 'A200', 'A400',
|
||||||
|
'A700'],
|
||||||
|
'LightGreen': ['50', '100', '200', '300', '400', '500', '600', 'A100',
|
||||||
|
'A200', 'A400', 'A700'],
|
||||||
|
'Lime': ['50', '100', '200', '300', '400', '500', '600', '700', '800',
|
||||||
|
'A100', 'A200', 'A400', 'A700'],
|
||||||
|
'Yellow': ['50', '100', '200', '300', '400', '500', '600', '700', '800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'],
|
||||||
|
'Amber': ['50', '100', '200', '300', '400', '500', '600', '700', '800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'],
|
||||||
|
'Orange': ['50', '100', '200', '300', '400', '500', '600', '700', 'A100',
|
||||||
|
'A200', 'A400', 'A700'],
|
||||||
|
'DeepOrange': ['50', '100', '200', '300', '400', 'A100', 'A200'],
|
||||||
|
'Brown': ['50', '100', '200'],
|
||||||
|
'Grey': ['51', '100', '200', '300', '400', '500'],
|
||||||
|
'BlueGrey': ['50', '100', '200', '300'],
|
||||||
|
'Dark': [],
|
||||||
|
'Light': ['White', 'MainBackground', 'DialogBackground']
|
||||||
|
}
|
||||||
|
|
||||||
|
level_bg_colors = [
|
||||||
|
'50',
|
||||||
|
'100',
|
||||||
|
'200',
|
||||||
|
'300',
|
||||||
|
]
|
||||||
|
|
||||||
|
level_selected_bg_colors = [
|
||||||
|
'400',
|
||||||
|
'500',
|
||||||
|
'600',
|
||||||
|
'700'
|
||||||
|
]
|
||||||
|
|
||||||
|
def getColors(style,level):
|
||||||
|
i = level % len(level_bg_colors)
|
||||||
|
text_color = text_colors[style][ level_bg_colors[i]]
|
||||||
|
bg_color = colors[style][ level_bg_colors[i]]
|
||||||
|
return color
|
||||||
|
|
||||||
|
def getSelectedColors(style,level):
|
||||||
|
i = level % len(level_selected_bg_colors)
|
||||||
|
text_color = text_colors[style][ level_bg_colors[i]]
|
||||||
|
bg_color = colors[style][ level_bg_colors[i]]
|
||||||
|
return text_color, bg_color
|
||||||
|
|
||||||
|
error_color_id = '900',
|
||||||
|
info_color_id = '800'
|
||||||
|
def getErrorColor(style):
|
||||||
|
text_color = text_colors[style][ error_color_id ]
|
||||||
|
bg_color = colors[style][ error_color_id ]
|
||||||
|
return text_color, bg_color
|
||||||
|
|
||||||
|
def getInfoColor(style):
|
||||||
|
text_color = text_colors[style][ info_color_id ]
|
||||||
|
bg_color = colors[style][ info_color_id ]
|
||||||
|
return text_color, bg_color
|
||||||
|
|
||||||
|
|
||||||
|
text_colors = {
|
||||||
|
'Red': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Pink': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': 'ffffff',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Purple': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': 'ffffff',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'DeepPurple': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': 'ffffff',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Indigo': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': 'ffffff',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Blue': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': 'ffffff',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'LightBlue': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Cyan': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Teal': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Green': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'LightGreen': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Lime': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': '000000',
|
||||||
|
'800': '000000',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Yellow': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': '000000',
|
||||||
|
'800': '000000',
|
||||||
|
'900': '000000',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Amber': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': '000000',
|
||||||
|
'800': '000000',
|
||||||
|
'900': '000000',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'Orange': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': '000000',
|
||||||
|
'700': '000000',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': '000000',
|
||||||
|
'A700': '000000',
|
||||||
|
},
|
||||||
|
'DeepOrange': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
'A100': '000000',
|
||||||
|
'A200': '000000',
|
||||||
|
'A400': 'ffffff',
|
||||||
|
'A700': 'ffffff',
|
||||||
|
},
|
||||||
|
'Brown': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': 'ffffff',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
},
|
||||||
|
'Grey': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': '000000',
|
||||||
|
'500': '000000',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
},
|
||||||
|
'BlueGrey': {
|
||||||
|
'50': '000000',
|
||||||
|
'100': '000000',
|
||||||
|
'200': '000000',
|
||||||
|
'300': '000000',
|
||||||
|
'400': 'ffffff',
|
||||||
|
'500': 'ffffff',
|
||||||
|
'600': 'ffffff',
|
||||||
|
'700': 'ffffff',
|
||||||
|
'800': 'ffffff',
|
||||||
|
'900': 'ffffff',
|
||||||
|
},
|
||||||
|
}
|
331
kivyblocks/date_picker.py
Normal file
331
kivyblocks/date_picker.py
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.uix.modalview import ModalView
|
||||||
|
from kivymd.label import MDLabel
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
|
from kivymd.backgroundcolorbehavior import SpecificBackgroundColorBehavior
|
||||||
|
from kivy.uix.floatlayout import FloatLayout
|
||||||
|
from kivymd.elevationbehavior import RectangularElevationBehavior
|
||||||
|
import calendar
|
||||||
|
from datetime import date
|
||||||
|
import datetime
|
||||||
|
from kivy.properties import StringProperty, NumericProperty, ObjectProperty, \
|
||||||
|
BooleanProperty, ListProperty
|
||||||
|
from kivy.uix.anchorlayout import AnchorLayout
|
||||||
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
|
from kivymd.ripplebehavior import CircularRippleBehavior
|
||||||
|
from kivy.clock import Clock
|
||||||
|
from kivy.core.window import Window
|
||||||
|
|
||||||
|
Builder.load_string("""
|
||||||
|
#:import calendar calendar
|
||||||
|
#:import platform platform
|
||||||
|
<MDDatePicker>
|
||||||
|
cal_layout: cal_layout
|
||||||
|
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: [dp(328), dp(484)] if self.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [dp(512), dp(304)]
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgb: app.theme_cls.primary_color
|
||||||
|
Rectangle:
|
||||||
|
size: [dp(328), dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [dp(168), dp(304)]
|
||||||
|
pos: [root.pos[0], root.pos[1] + root.height-dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [root.pos[0], root.pos[1] + root.height-dp(304)]
|
||||||
|
Color:
|
||||||
|
rgb: app.theme_cls.bg_normal
|
||||||
|
Rectangle:
|
||||||
|
size: [dp(328), dp(484)-dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [dp(344), dp(304)]
|
||||||
|
pos: [root.pos[0], root.pos[1] + root.height-dp(96)-(dp(484)-dp(96))]\
|
||||||
|
if self.theme_cls.device_orientation == 'portrait' else [root.pos[0]+dp(168), root.pos[1]] #+dp(334)
|
||||||
|
MDLabel:
|
||||||
|
id: label_full_date
|
||||||
|
font_style: 'Display1'
|
||||||
|
text_color: root.specific_text_color
|
||||||
|
theme_text_color: 'Custom'
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: [root.width, dp(30)] if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [dp(168), dp(30)]
|
||||||
|
pos: [root.pos[0]+dp(23), root.pos[1] + root.height - dp(74)] \
|
||||||
|
if root.theme_cls.device_orientation == 'portrait' \
|
||||||
|
else [root.pos[0]+dp(3), root.pos[1] + dp(214)]
|
||||||
|
line_height: 0.84
|
||||||
|
valign: 'middle'
|
||||||
|
text_size: [root.width, None] if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [dp(149), None]
|
||||||
|
bold: True
|
||||||
|
text: root.fmt_lbl_date(root.sel_year, root.sel_month, root.sel_day, root.theme_cls.device_orientation)
|
||||||
|
MDLabel:
|
||||||
|
id: label_year
|
||||||
|
font_style: 'Subhead'
|
||||||
|
text_color: root.specific_text_color
|
||||||
|
theme_text_color: 'Custom'
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: root.width, dp(30)
|
||||||
|
pos: (root.pos[0]+dp(23), root.pos[1]+root.height-dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (root.pos[0]+dp(16), root.pos[1]+root.height-dp(41))
|
||||||
|
valign: 'middle'
|
||||||
|
text: str(root.sel_year)
|
||||||
|
GridLayout:
|
||||||
|
id: cal_layout
|
||||||
|
cols: 7
|
||||||
|
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)
|
||||||
|
size_hint: (None, None)
|
||||||
|
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: (root.pos[0]+dp(10), root.pos[1]+dp(60)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (root.pos[0]+dp(168)+dp(8), root.pos[1]+dp(48))
|
||||||
|
MDLabel:
|
||||||
|
id: label_month_selector
|
||||||
|
font_style: 'Body2'
|
||||||
|
text: calendar.month_name[root.month].capitalize() + ' ' + str(root.year)
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: root.width, dp(30)
|
||||||
|
pos: root.pos
|
||||||
|
theme_text_color: 'Primary'
|
||||||
|
pos_hint: {'center_x': 0.5, 'center_y': 0.75} if self.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else {'center_x': 0.67, 'center_y': 0.915}
|
||||||
|
valign: "middle"
|
||||||
|
halign: "center"
|
||||||
|
MDIconButton:
|
||||||
|
icon: 'chevron-left'
|
||||||
|
theme_text_color: 'Secondary'
|
||||||
|
pos_hint: {'center_x': 0.08, 'center_y': 0.745} if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else {'center_x': 0.39, 'center_y': 0.925}
|
||||||
|
on_release: root.change_month('prev')
|
||||||
|
MDIconButton:
|
||||||
|
icon: 'chevron-right'
|
||||||
|
theme_text_color: 'Secondary'
|
||||||
|
pos_hint: {'center_x': 0.92, 'center_y': 0.745} if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else {'center_x': 0.94, 'center_y': 0.925}
|
||||||
|
on_release: root.change_month('next')
|
||||||
|
MDFlatButton:
|
||||||
|
width: dp(32)
|
||||||
|
id: ok_button
|
||||||
|
pos: root.pos[0]+root.size[0]-self.width-dp(10), root.pos[1] + dp(10)
|
||||||
|
text: "OK"
|
||||||
|
on_release: root.ok_click()
|
||||||
|
MDFlatButton:
|
||||||
|
id: cancel_button
|
||||||
|
pos: root.pos[0]+root.size[0]-self.width-ok_button.width-dp(10), root.pos[1] + dp(10)
|
||||||
|
text: "Cancel"
|
||||||
|
on_release: root.dismiss()
|
||||||
|
|
||||||
|
<DayButton>
|
||||||
|
size_hint: None, None
|
||||||
|
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (dp(32), dp(32))
|
||||||
|
MDLabel:
|
||||||
|
font_style: 'Caption'
|
||||||
|
theme_text_color: 'Custom' if root.is_today and not root.is_selected else 'Primary'
|
||||||
|
text_color: root.theme_cls.primary_color
|
||||||
|
opposite_colors: root.is_selected if root.owner.sel_month == root.owner.month \
|
||||||
|
and root.owner.sel_year == root.owner.year and str(self.text) == str(root.owner.sel_day) else False
|
||||||
|
size_hint_x: None
|
||||||
|
valign: 'middle'
|
||||||
|
halign: 'center'
|
||||||
|
text: root.text
|
||||||
|
|
||||||
|
<WeekdayLabel>
|
||||||
|
font_style: 'Caption'
|
||||||
|
theme_text_color: 'Secondary'
|
||||||
|
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (dp(32), dp(32))
|
||||||
|
size_hint: None, None
|
||||||
|
text_size: self.size
|
||||||
|
valign: 'middle' if root.theme_cls.device_orientation == 'portrait' else 'bottom'
|
||||||
|
halign: 'center'
|
||||||
|
|
||||||
|
<DaySelector>
|
||||||
|
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (dp(32), dp(32))
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: self.theme_cls.primary_color if self.shown else [0, 0, 0, 0]
|
||||||
|
Ellipse:
|
||||||
|
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else (dp(32), dp(32))
|
||||||
|
pos: self.pos if root.theme_cls.device_orientation == 'portrait'\
|
||||||
|
else [self.pos[0], self.pos[1]]
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
class DaySelector(ThemableBehavior, AnchorLayout):
|
||||||
|
shown = BooleanProperty(False)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(DaySelector, self).__init__()
|
||||||
|
self.parent_class = parent
|
||||||
|
self.parent_class.add_widget(self, index=7)
|
||||||
|
self.selected_widget = None
|
||||||
|
Window.bind(on_resize=self.move_resize)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
parent = self.parent_class
|
||||||
|
if parent.sel_month == parent.month and parent.sel_year == parent.year:
|
||||||
|
self.shown = True
|
||||||
|
else:
|
||||||
|
self.shown = False
|
||||||
|
|
||||||
|
def set_widget(self, widget):
|
||||||
|
self.selected_widget = widget
|
||||||
|
self.pos = widget.pos
|
||||||
|
self.move_resize(do_again=True)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def move_resize(self, window=None, width=None, height=None, do_again=True):
|
||||||
|
self.pos = self.selected_widget.pos
|
||||||
|
if do_again:
|
||||||
|
Clock.schedule_once(lambda x: self.move_resize(do_again=False), 0.01)
|
||||||
|
|
||||||
|
|
||||||
|
class DayButton(ThemableBehavior, CircularRippleBehavior, ButtonBehavior,
|
||||||
|
AnchorLayout):
|
||||||
|
text = StringProperty()
|
||||||
|
owner = ObjectProperty()
|
||||||
|
is_today = BooleanProperty(False)
|
||||||
|
is_selected = BooleanProperty(False)
|
||||||
|
|
||||||
|
def on_release(self):
|
||||||
|
self.owner.set_selected_widget(self)
|
||||||
|
|
||||||
|
|
||||||
|
class WeekdayLabel(MDLabel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MDDatePicker(FloatLayout, ThemableBehavior, RectangularElevationBehavior,
|
||||||
|
SpecificBackgroundColorBehavior, ModalView):
|
||||||
|
_sel_day_widget = ObjectProperty()
|
||||||
|
cal_list = None
|
||||||
|
cal_layout = ObjectProperty()
|
||||||
|
sel_year = NumericProperty()
|
||||||
|
sel_month = NumericProperty()
|
||||||
|
sel_day = NumericProperty()
|
||||||
|
day = NumericProperty()
|
||||||
|
month = NumericProperty()
|
||||||
|
year = NumericProperty()
|
||||||
|
today = date.today()
|
||||||
|
callback = ObjectProperty()
|
||||||
|
background_color = ListProperty([0, 0, 0, 0.7])
|
||||||
|
|
||||||
|
class SetDateError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, callback, year=None, month=None, day=None,
|
||||||
|
firstweekday=0,
|
||||||
|
**kwargs):
|
||||||
|
self.callback = callback
|
||||||
|
self.cal = 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(MDDatePicker, self).__init__(**kwargs)
|
||||||
|
self.selector = DaySelector(parent=self)
|
||||||
|
self.generate_cal_widgets()
|
||||||
|
self.update_cal_matrix(self.sel_year, self.sel_month)
|
||||||
|
self.set_month_day(self.sel_day)
|
||||||
|
self.selector.update()
|
||||||
|
|
||||||
|
def ok_click(self):
|
||||||
|
self.callback(date(self.sel_year, self.sel_month, self.sel_day))
|
||||||
|
self.dismiss()
|
||||||
|
|
||||||
|
def fmt_lbl_date(self, year, month, day, orientation):
|
||||||
|
d = datetime.date(int(year), int(month), int(day))
|
||||||
|
separator = '\n' if orientation == 'landscape' else ' '
|
||||||
|
return d.strftime('%a,').capitalize() + separator + d.strftime(
|
||||||
|
'%b').capitalize() + ' ' + str(day).lstrip('0')
|
||||||
|
|
||||||
|
def set_date(self, year, month, day):
|
||||||
|
try:
|
||||||
|
date(year, month, day)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
if str(e) == "day is out of range for month":
|
||||||
|
raise self.SetDateError(" Day %s day is out of range for month %s" % (day, month))
|
||||||
|
elif str(e) == "month must be in 1..12":
|
||||||
|
raise self.SetDateError("Month must be between 1 and 12, got %s" % month)
|
||||||
|
elif str(e) == "year is out of range":
|
||||||
|
raise self.SetDateError("Year must be between %s and %s, got %s" %
|
||||||
|
(datetime.MINYEAR, datetime.MAXYEAR, year))
|
||||||
|
else:
|
||||||
|
self.sel_year = year
|
||||||
|
self.sel_month = month
|
||||||
|
self.sel_day = day
|
||||||
|
self.month = self.sel_month
|
||||||
|
self.year = self.sel_year
|
||||||
|
self.day = self.sel_day
|
||||||
|
self.update_cal_matrix(self.sel_year, self.sel_month)
|
||||||
|
self.set_month_day(self.sel_day)
|
||||||
|
self.selector.update()
|
||||||
|
|
||||||
|
def set_selected_widget(self, widget):
|
||||||
|
if self._sel_day_widget:
|
||||||
|
self._sel_day_widget.is_selected = False
|
||||||
|
widget.is_selected = True
|
||||||
|
self.sel_month = int(self.month)
|
||||||
|
self.sel_year = int(self.year)
|
||||||
|
self.sel_day = int(widget.text)
|
||||||
|
self._sel_day_widget = widget
|
||||||
|
self.selector.set_widget(widget)
|
||||||
|
|
||||||
|
def set_month_day(self, day):
|
||||||
|
for idx in range(len(self.cal_list)):
|
||||||
|
if str(day) == str(self.cal_list[idx].text):
|
||||||
|
self._sel_day_widget = self.cal_list[idx]
|
||||||
|
self.sel_day = int(self.cal_list[idx].text)
|
||||||
|
if self._sel_day_widget:
|
||||||
|
self._sel_day_widget.is_selected = False
|
||||||
|
self._sel_day_widget = self.cal_list[idx]
|
||||||
|
self.cal_list[idx].is_selected = True
|
||||||
|
self.selector.set_widget(self.cal_list[idx])
|
||||||
|
|
||||||
|
def update_cal_matrix(self, year, month):
|
||||||
|
try:
|
||||||
|
dates = [x for x in self.cal.itermonthdates(year, month)]
|
||||||
|
except ValueError as e:
|
||||||
|
if str(e) == "year is out of range":
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.year = year
|
||||||
|
self.month = month
|
||||||
|
for idx in range(len(self.cal_list)):
|
||||||
|
if idx >= len(dates) or dates[idx].month != month:
|
||||||
|
self.cal_list[idx].disabled = True
|
||||||
|
self.cal_list[idx].text = ''
|
||||||
|
else:
|
||||||
|
self.cal_list[idx].disabled = False
|
||||||
|
self.cal_list[idx].text = str(dates[idx].day)
|
||||||
|
self.cal_list[idx].is_today = dates[idx] == self.today
|
||||||
|
self.selector.update()
|
||||||
|
|
||||||
|
def generate_cal_widgets(self):
|
||||||
|
cal_list = []
|
||||||
|
for i in calendar.day_abbr:
|
||||||
|
self.cal_layout.add_widget(WeekdayLabel(text=i[0].upper()))
|
||||||
|
for i in range(6 * 7): # 6 weeks, 7 days a week
|
||||||
|
db = DayButton(owner=self)
|
||||||
|
cal_list.append(db)
|
||||||
|
self.cal_layout.add_widget(db)
|
||||||
|
self.cal_list = cal_list
|
||||||
|
|
||||||
|
def change_month(self, operation):
|
||||||
|
op = 1 if operation is 'next' else -1
|
||||||
|
sl, sy = self.month, self.year
|
||||||
|
m = 12 if sl + op == 0 else 1 if sl + op == 13 else sl + op
|
||||||
|
y = sy - 1 if sl + op == 0 else sy + 1 if sl + op == 13 else sy
|
||||||
|
self.update_cal_matrix(y, m)
|
@ -119,7 +119,6 @@ class Row(GridLayout):
|
|||||||
self.part.datagrid.select_rowid = self.row_id
|
self.part.datagrid.select_rowid = self.row_id
|
||||||
self.part.datagrid.dispatch('on_selected',self)
|
self.part.datagrid.dispatch('on_selected',self)
|
||||||
|
|
||||||
|
|
||||||
class Header(WidgetReady, ScrollWidget):
|
class Header(WidgetReady, ScrollWidget):
|
||||||
def __init__(self,part,**kw):
|
def __init__(self,part,**kw):
|
||||||
self.part = part
|
self.part = part
|
||||||
|
50
kivyblocks/material_resources.py
Normal file
50
kivyblocks/material_resources.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from kivy import platform
|
||||||
|
from kivy.core.window import Window
|
||||||
|
from kivy.metrics import dp
|
||||||
|
from kivyblocks import fonts_path
|
||||||
|
|
||||||
|
# Feel free to override this const if you're designing for a device such as
|
||||||
|
# a GNU/Linux tablet.
|
||||||
|
if platform != "android" and platform != "ios":
|
||||||
|
DEVICE_TYPE = "desktop"
|
||||||
|
elif Window.width >= dp(600) and Window.height >= dp(600):
|
||||||
|
DEVICE_TYPE = "tablet"
|
||||||
|
else:
|
||||||
|
DEVICE_TYPE = "mobile"
|
||||||
|
|
||||||
|
if DEVICE_TYPE == "mobile":
|
||||||
|
MAX_NAV_DRAWER_WIDTH = dp(300)
|
||||||
|
HORIZ_MARGINS = dp(16)
|
||||||
|
STANDARD_INCREMENT = dp(56)
|
||||||
|
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
||||||
|
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT - dp(8)
|
||||||
|
else:
|
||||||
|
MAX_NAV_DRAWER_WIDTH = dp(400)
|
||||||
|
HORIZ_MARGINS = dp(24)
|
||||||
|
STANDARD_INCREMENT = dp(64)
|
||||||
|
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
||||||
|
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
||||||
|
|
||||||
|
TOUCH_TARGET_HEIGHT = dp(48)
|
||||||
|
|
||||||
|
FONTS = [
|
||||||
|
{
|
||||||
|
"name": "Roboto",
|
||||||
|
"fn_regular": fonts_path + 'Roboto-Regular.ttf',
|
||||||
|
"fn_bold": fonts_path + 'Roboto-Medium.ttf',
|
||||||
|
"fn_italic": fonts_path + 'Roboto-Italic.ttf',
|
||||||
|
"fn_bolditalic": fonts_path + 'Roboto-MediumItalic.ttf'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RobotoLight",
|
||||||
|
"fn_regular": fonts_path + 'Roboto-Thin.ttf',
|
||||||
|
"fn_bold": fonts_path + 'Roboto-Light.ttf',
|
||||||
|
"fn_italic": fonts_path + 'Roboto-ThinItalic.ttf',
|
||||||
|
"fn_bolditalic": fonts_path + 'Roboto-LightItalic.ttf'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Icons",
|
||||||
|
"fn_regular": fonts_path + 'materialdesignicons-webfont.ttf'
|
||||||
|
}
|
||||||
|
]
|
383
kivyblocks/theme_picker.py
Normal file
383
kivyblocks/theme_picker.py
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.properties import OptionProperty
|
||||||
|
from kivy.uix.floatlayout import FloatLayout
|
||||||
|
from kivy.uix.modalview import ModalView
|
||||||
|
from kivy.utils import get_color_from_hex
|
||||||
|
from kivymd.backgroundcolorbehavior import SpecificBackgroundColorBehavior
|
||||||
|
from kivymd.button import MDIconButton
|
||||||
|
from kivymd.color_definitions import colors
|
||||||
|
from kivymd.elevationbehavior import RectangularElevationBehavior
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
|
|
||||||
|
Builder.load_string("""
|
||||||
|
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel
|
||||||
|
#:import MDTab kivymd.tabs.MDTab
|
||||||
|
<ColorSelector>:
|
||||||
|
size: dp(40), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: root.rgb_hex(root.color_name)
|
||||||
|
Ellipse:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
|
||||||
|
<AccentColorSelector@ColorSelector>:
|
||||||
|
on_release: app.theme_cls.accent_palette = root.color_name
|
||||||
|
|
||||||
|
<PrimaryColorSelector@ColorSelector>:
|
||||||
|
on_release: app.theme_cls.primary_palette = root.color_name
|
||||||
|
|
||||||
|
<MDThemePicker>:
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: dp(284), dp(120)+dp(290)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgb: app.theme_cls.primary_color
|
||||||
|
Rectangle:
|
||||||
|
size: self.width, dp(120)
|
||||||
|
pos: root.pos[0], root.pos[1] + root.height-dp(120)
|
||||||
|
Color:
|
||||||
|
rgb: app.theme_cls.bg_normal
|
||||||
|
Rectangle:
|
||||||
|
size: self.width, dp(290)
|
||||||
|
pos: root.pos[0], root.pos[1] + root.height-(dp(120)+dp(290))
|
||||||
|
|
||||||
|
MDFlatButton:
|
||||||
|
pos: root.pos[0]+root.size[0]-self.width-dp(10), root.pos[1] + dp(10)
|
||||||
|
text: "Close"
|
||||||
|
on_release: root.dismiss()
|
||||||
|
MDLabel:
|
||||||
|
font_style: "Headline"
|
||||||
|
text: "Change theme"
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: dp(160), dp(50)
|
||||||
|
pos_hint: {'center_x': 0.5, 'center_y': 0.9}
|
||||||
|
theme_text_color: 'Custom'
|
||||||
|
text_color: root.specific_text_color
|
||||||
|
MDTabbedPanel:
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: root.width, root.height-dp(135)
|
||||||
|
pos_hint: {'center_x': 0.5, 'center_y': 0.475}
|
||||||
|
id: tab_panel
|
||||||
|
tab_display_mode:'text'
|
||||||
|
|
||||||
|
MDTab:
|
||||||
|
name: 'color'
|
||||||
|
text: "Theme Color"
|
||||||
|
BoxLayout:
|
||||||
|
spacing: dp(4)
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: dp(270), root.height # -dp(120)
|
||||||
|
pos_hint: {'center_x': 0.532, 'center_y': 0.89}
|
||||||
|
orientation: 'vertical'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Red'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Pink'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Purple'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'DeepPurple'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Indigo'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Blue'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'LightBlue'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Cyan'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Teal'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Green'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'LightGreen'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Lime'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
orientation: 'horizontal'
|
||||||
|
halign: 'center'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Yellow'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Amber'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Orange'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'DeepOrange'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
#pos: self.pos
|
||||||
|
orientation: 'horizontal'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Brown'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'Grey'
|
||||||
|
BoxLayout:
|
||||||
|
PrimaryColorSelector:
|
||||||
|
color_name: 'BlueGrey'
|
||||||
|
BoxLayout:
|
||||||
|
MDIconButton:
|
||||||
|
size: dp(40), dp(40)
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: app.theme_cls.bg_normal
|
||||||
|
Ellipse:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
disabled: True
|
||||||
|
|
||||||
|
MDTab:
|
||||||
|
name: 'accent_color'
|
||||||
|
text: "Accent Color"
|
||||||
|
BoxLayout:
|
||||||
|
spacing: dp(4)
|
||||||
|
size_hint: (None, None)
|
||||||
|
size: dp(270), root.height # -dp(120)
|
||||||
|
pos_hint: {'center_x': 0.532, 'center_y': 0.89}
|
||||||
|
orientation: 'vertical'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Red'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Pink'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Purple'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'DeepPurple'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Indigo'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Blue'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'LightBlue'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Cyan'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
halign: 'center'
|
||||||
|
orientation: 'horizontal'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Teal'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Green'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'LightGreen'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Lime'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
pos: self.pos
|
||||||
|
orientation: 'horizontal'
|
||||||
|
halign: 'center'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Yellow'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Amber'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Orange'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'DeepOrange'
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
||||||
|
size: dp(230), dp(40)
|
||||||
|
#pos: self.pos
|
||||||
|
orientation: 'horizontal'
|
||||||
|
padding: 0, 0, 0, dp(1)
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Brown'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'Grey'
|
||||||
|
BoxLayout:
|
||||||
|
AccentColorSelector:
|
||||||
|
color_name: 'BlueGrey'
|
||||||
|
BoxLayout:
|
||||||
|
MDIconButton:
|
||||||
|
size: dp(40), dp(40)
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: app.theme_cls.bg_normal
|
||||||
|
Ellipse:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
disabled: True
|
||||||
|
|
||||||
|
MDTab:
|
||||||
|
name: 'style'
|
||||||
|
text: "Theme Style"
|
||||||
|
FloatLayout:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (None, None)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': .6}
|
||||||
|
halign: 'center'
|
||||||
|
valign: 'center'
|
||||||
|
spacing: dp(10)
|
||||||
|
width: dp(210)
|
||||||
|
height: dp(100)
|
||||||
|
MDIconButton:
|
||||||
|
size: dp(100), dp(100)
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: 1, 1, 1, 1
|
||||||
|
Ellipse:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
Color:
|
||||||
|
rgba: 0, 0, 0, 1
|
||||||
|
Line:
|
||||||
|
width: 1.
|
||||||
|
circle: (self.center_x, self.center_y, dp(50))
|
||||||
|
on_release: app.theme_cls.theme_style = 'Light'
|
||||||
|
MDIconButton:
|
||||||
|
size: dp(100), dp(100)
|
||||||
|
pos: self.pos
|
||||||
|
size_hint: (None, None)
|
||||||
|
canvas:
|
||||||
|
Color:
|
||||||
|
rgba: 0, 0, 0, 1
|
||||||
|
Ellipse:
|
||||||
|
size: self.size
|
||||||
|
pos: self.pos
|
||||||
|
on_release: app.theme_cls.theme_style = 'Dark'
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
class ColorSelector(MDIconButton):
|
||||||
|
color_name = OptionProperty(
|
||||||
|
'Indigo',
|
||||||
|
options=['Red', 'Pink', 'Purple', 'DeepPurple', 'Indigo', 'Blue',
|
||||||
|
'LightBlue', 'Cyan', 'Teal', 'Green', 'LightGreen',
|
||||||
|
'Lime', 'Yellow', 'Amber', 'Orange', 'DeepOrange',
|
||||||
|
'Brown', 'Grey', 'BlueGrey'])
|
||||||
|
|
||||||
|
def rgb_hex(self, col):
|
||||||
|
return get_color_from_hex(colors[col][self.theme_cls.accent_hue])
|
||||||
|
|
||||||
|
|
||||||
|
class MDThemePicker(ThemableBehavior, FloatLayout, ModalView,
|
||||||
|
SpecificBackgroundColorBehavior,
|
||||||
|
RectangularElevationBehavior):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from kivy.app import App
|
||||||
|
from kivymd.theming import ThemeManager
|
||||||
|
|
||||||
|
class ThemePickerApp(App):
|
||||||
|
theme_cls = ThemeManager()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
main_widget = Builder.load_string("""
|
||||||
|
#:import MDRaisedButton kivymd.button.MDRaisedButton
|
||||||
|
#:import MDThemePicker kivymd.theme_picker.MDThemePicker
|
||||||
|
FloatLayout:
|
||||||
|
MDRaisedButton:
|
||||||
|
size_hint: None, None
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||||
|
size: 3 * dp(48), dp(48)
|
||||||
|
center_x: self.parent.center_x
|
||||||
|
text: 'Open theme picker'
|
||||||
|
on_release: MDThemePicker().open()
|
||||||
|
opposite_colors: True
|
||||||
|
""")
|
||||||
|
return main_widget
|
||||||
|
|
||||||
|
ThemePickerApp().run()
|
351
kivyblocks/theming.py
Normal file
351
kivyblocks/theming.py
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from kivy.app import App
|
||||||
|
from kivy.core.text import LabelBase
|
||||||
|
from kivy.core.window import Window
|
||||||
|
from kivy.clock import Clock
|
||||||
|
from kivy.metrics import dp
|
||||||
|
from kivy.properties import OptionProperty, AliasProperty, ObjectProperty, \
|
||||||
|
StringProperty, ListProperty, BooleanProperty
|
||||||
|
from kivy.uix.widget import Widget
|
||||||
|
from kivy.utils import get_color_from_hex
|
||||||
|
from kivy.atlas import Atlas
|
||||||
|
from kivyblocks.color_definitions import colors, text_colors
|
||||||
|
from kivyblocks.material_resources import FONTS, DEVICE_TYPE
|
||||||
|
from kivyblocks import images_path
|
||||||
|
|
||||||
|
for font in FONTS:
|
||||||
|
LabelBase.register(**font)
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeManager(Widget):
|
||||||
|
primary_palette = OptionProperty(
|
||||||
|
'Blue',
|
||||||
|
options=['Pink', 'Blue', 'Indigo', 'BlueGrey', 'Brown',
|
||||||
|
'LightBlue',
|
||||||
|
'Purple', 'Grey', 'Yellow', 'LightGreen', 'DeepOrange',
|
||||||
|
'Green', 'Red', 'Teal', 'Orange', 'Cyan', 'Amber',
|
||||||
|
'DeepPurple', 'Lime'])
|
||||||
|
|
||||||
|
primary_hue = OptionProperty(
|
||||||
|
'500',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
primary_light_hue = OptionProperty(
|
||||||
|
'200',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
primary_dark_hue = OptionProperty(
|
||||||
|
'700',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
def _get_primary_color(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.primary_palette][self.primary_hue])
|
||||||
|
|
||||||
|
primary_color = AliasProperty(_get_primary_color,
|
||||||
|
bind=('primary_palette', 'primary_hue'))
|
||||||
|
|
||||||
|
def _get_primary_light(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.primary_palette][self.primary_light_hue])
|
||||||
|
|
||||||
|
primary_light = AliasProperty(
|
||||||
|
_get_primary_light, bind=('primary_palette', 'primary_light_hue'))
|
||||||
|
|
||||||
|
def _get_primary_dark(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.primary_palette][self.primary_dark_hue])
|
||||||
|
|
||||||
|
primary_dark = AliasProperty(_get_primary_dark,
|
||||||
|
bind=('primary_palette', 'primary_dark_hue'))
|
||||||
|
|
||||||
|
accent_palette = OptionProperty(
|
||||||
|
'Amber',
|
||||||
|
options=['Pink', 'Blue', 'Indigo', 'BlueGrey', 'Brown',
|
||||||
|
'LightBlue',
|
||||||
|
'Purple', 'Grey', 'Yellow', 'LightGreen', 'DeepOrange',
|
||||||
|
'Green', 'Red', 'Teal', 'Orange', 'Cyan', 'Amber',
|
||||||
|
'DeepPurple', 'Lime'])
|
||||||
|
|
||||||
|
accent_hue = OptionProperty(
|
||||||
|
'500',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
accent_light_hue = OptionProperty(
|
||||||
|
'200',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
accent_dark_hue = OptionProperty(
|
||||||
|
'700',
|
||||||
|
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
||||||
|
'800',
|
||||||
|
'900', 'A100', 'A200', 'A400', 'A700'])
|
||||||
|
|
||||||
|
def _get_accent_color(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.accent_palette][self.accent_hue])
|
||||||
|
|
||||||
|
accent_color = AliasProperty(_get_accent_color,
|
||||||
|
bind=['accent_palette', 'accent_hue'])
|
||||||
|
|
||||||
|
def _get_accent_light(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.accent_palette][self.accent_light_hue])
|
||||||
|
|
||||||
|
accent_light = AliasProperty(_get_accent_light,
|
||||||
|
bind=['accent_palette', 'accent_light_hue'])
|
||||||
|
|
||||||
|
def _get_accent_dark(self):
|
||||||
|
return get_color_from_hex(
|
||||||
|
colors[self.accent_palette][self.accent_dark_hue])
|
||||||
|
|
||||||
|
accent_dark = AliasProperty(_get_accent_dark,
|
||||||
|
bind=['accent_palette', 'accent_dark_hue'])
|
||||||
|
|
||||||
|
theme_style = OptionProperty('Light', options=['Light', 'Dark'])
|
||||||
|
|
||||||
|
def _get_theme_style(self, opposite):
|
||||||
|
if opposite:
|
||||||
|
return 'Light' if self.theme_style == 'Dark' else 'Dark'
|
||||||
|
else:
|
||||||
|
return self.theme_style
|
||||||
|
|
||||||
|
def _get_bg_darkest(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
return get_color_from_hex(colors['Light']['StatusBar'])
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
return get_color_from_hex(colors['Dark']['StatusBar'])
|
||||||
|
|
||||||
|
bg_darkest = AliasProperty(_get_bg_darkest, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_bg_darkest(self):
|
||||||
|
return self._get_bg_darkest(True)
|
||||||
|
|
||||||
|
opposite_bg_darkest = AliasProperty(_get_op_bg_darkest,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_bg_dark(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
return get_color_from_hex(colors['Light']['AppBar'])
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
return get_color_from_hex(colors['Dark']['AppBar'])
|
||||||
|
|
||||||
|
bg_dark = AliasProperty(_get_bg_dark, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_bg_dark(self):
|
||||||
|
return self._get_bg_dark(True)
|
||||||
|
|
||||||
|
opposite_bg_dark = AliasProperty(_get_op_bg_dark, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_bg_normal(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
return get_color_from_hex(colors['Light']['Background'])
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
return get_color_from_hex(colors['Dark']['Background'])
|
||||||
|
|
||||||
|
bg_normal = AliasProperty(_get_bg_normal, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_bg_normal(self):
|
||||||
|
return self._get_bg_normal(True)
|
||||||
|
|
||||||
|
opposite_bg_normal = AliasProperty(_get_op_bg_normal, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_bg_light(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
return get_color_from_hex(colors['Light']['CardsDialogs'])
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
return get_color_from_hex(colors['Dark']['CardsDialogs'])
|
||||||
|
|
||||||
|
bg_light = AliasProperty(_get_bg_light, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_bg_light(self):
|
||||||
|
return self._get_bg_light(True)
|
||||||
|
|
||||||
|
opposite_bg_light = AliasProperty(_get_op_bg_light, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_divider_color(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
color = get_color_from_hex('000000')
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
color = get_color_from_hex('FFFFFF')
|
||||||
|
color[3] = .12
|
||||||
|
return color
|
||||||
|
|
||||||
|
divider_color = AliasProperty(_get_divider_color, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_divider_color(self):
|
||||||
|
return self._get_divider_color(True)
|
||||||
|
|
||||||
|
opposite_divider_color = AliasProperty(_get_op_divider_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_text_color(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
color = get_color_from_hex('000000')
|
||||||
|
color[3] = .87
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
color = get_color_from_hex('FFFFFF')
|
||||||
|
return color
|
||||||
|
|
||||||
|
text_color = AliasProperty(_get_text_color, bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_text_color(self):
|
||||||
|
return self._get_text_color(True)
|
||||||
|
|
||||||
|
opposite_text_color = AliasProperty(_get_op_text_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_secondary_text_color(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
color = get_color_from_hex('000000')
|
||||||
|
color[3] = .54
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
color = get_color_from_hex('FFFFFF')
|
||||||
|
color[3] = .70
|
||||||
|
return color
|
||||||
|
|
||||||
|
secondary_text_color = AliasProperty(_get_secondary_text_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_secondary_text_color(self):
|
||||||
|
return self._get_secondary_text_color(True)
|
||||||
|
|
||||||
|
opposite_secondary_text_color = AliasProperty(_get_op_secondary_text_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_icon_color(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
color = get_color_from_hex('000000')
|
||||||
|
color[3] = .54
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
color = get_color_from_hex('FFFFFF')
|
||||||
|
return color
|
||||||
|
|
||||||
|
icon_color = AliasProperty(_get_icon_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_icon_color(self):
|
||||||
|
return self._get_icon_color(True)
|
||||||
|
|
||||||
|
opposite_icon_color = AliasProperty(_get_op_icon_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_disabled_hint_text_color(self, opposite=False):
|
||||||
|
theme_style = self._get_theme_style(opposite)
|
||||||
|
if theme_style == 'Light':
|
||||||
|
color = get_color_from_hex('000000')
|
||||||
|
color[3] = .38
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
color = get_color_from_hex('FFFFFF')
|
||||||
|
color[3] = .50
|
||||||
|
return color
|
||||||
|
|
||||||
|
disabled_hint_text_color = AliasProperty(_get_disabled_hint_text_color,
|
||||||
|
bind=['theme_style'])
|
||||||
|
|
||||||
|
def _get_op_disabled_hint_text_color(self):
|
||||||
|
return self._get_disabled_hint_text_color(True)
|
||||||
|
|
||||||
|
opposite_disabled_hint_text_color = AliasProperty(
|
||||||
|
_get_op_disabled_hint_text_color, bind=['theme_style'])
|
||||||
|
|
||||||
|
# Hardcoded because muh standard
|
||||||
|
def _get_error_color(self):
|
||||||
|
return get_color_from_hex(colors['Red']['A700'])
|
||||||
|
|
||||||
|
error_color = AliasProperty(_get_error_color)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_ripple_color(self):
|
||||||
|
return self._ripple_color
|
||||||
|
|
||||||
|
def _set_ripple_color(self, value):
|
||||||
|
self._ripple_color = value
|
||||||
|
|
||||||
|
_ripple_color = ListProperty(get_color_from_hex(colors['Grey']['400']))
|
||||||
|
ripple_color = AliasProperty(_get_ripple_color,
|
||||||
|
_set_ripple_color,
|
||||||
|
bind=['_ripple_color'])
|
||||||
|
|
||||||
|
def _determine_device_orientation(self, _, window_size):
|
||||||
|
if window_size[0] > window_size[1]:
|
||||||
|
self.device_orientation = 'landscape'
|
||||||
|
elif window_size[1] >= window_size[0]:
|
||||||
|
self.device_orientation = 'portrait'
|
||||||
|
|
||||||
|
device_orientation = StringProperty('')
|
||||||
|
|
||||||
|
def _get_standard_increment(self):
|
||||||
|
if DEVICE_TYPE == 'mobile':
|
||||||
|
if self.device_orientation == 'landscape':
|
||||||
|
return dp(48)
|
||||||
|
else:
|
||||||
|
return dp(56)
|
||||||
|
else:
|
||||||
|
return dp(64)
|
||||||
|
|
||||||
|
standard_increment = AliasProperty(_get_standard_increment,
|
||||||
|
bind=['device_orientation'])
|
||||||
|
|
||||||
|
def _get_horizontal_margins(self):
|
||||||
|
if DEVICE_TYPE == 'mobile':
|
||||||
|
return dp(16)
|
||||||
|
else:
|
||||||
|
return dp(24)
|
||||||
|
|
||||||
|
horizontal_margins = AliasProperty(_get_horizontal_margins)
|
||||||
|
|
||||||
|
def on_theme_style(self, instance, value):
|
||||||
|
if hasattr(App.get_running_app(), 'theme_cls') and \
|
||||||
|
App.get_running_app().theme_cls == self:
|
||||||
|
self.set_clearcolor_by_theme_style(value)
|
||||||
|
|
||||||
|
def set_clearcolor_by_theme_style(self, theme_style):
|
||||||
|
if theme_style == 'Light':
|
||||||
|
Window.clearcolor = get_color_from_hex(
|
||||||
|
colors['Light']['Background'])
|
||||||
|
elif theme_style == 'Dark':
|
||||||
|
Window.clearcolor = get_color_from_hex(
|
||||||
|
colors['Dark']['Background'])
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(ThemeManager, self).__init__(**kwargs)
|
||||||
|
self.rec_shadow = Atlas('{}rec_shadow.atlas'.format(images_path))
|
||||||
|
self.rec_st_shadow = Atlas('{}rec_st_shadow.atlas'.format(images_path))
|
||||||
|
self.quad_shadow = Atlas('{}quad_shadow.atlas'.format(images_path))
|
||||||
|
self.round_shadow = Atlas('{}round_shadow.atlas'.format(images_path))
|
||||||
|
Clock.schedule_once(lambda x: self.on_theme_style(0, self.theme_style))
|
||||||
|
self._determine_device_orientation(None, Window.size)
|
||||||
|
Window.bind(size=self._determine_device_orientation)
|
||||||
|
|
||||||
|
|
||||||
|
class ThemableBehavior(object):
|
||||||
|
theme_cls = ObjectProperty(None)
|
||||||
|
opposite_colors = BooleanProperty(False)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
if self.theme_cls is not None:
|
||||||
|
pass
|
||||||
|
elif hasattr(App.get_running_app(), 'theme_cls'):
|
||||||
|
self.theme_cls = App.get_running_app().theme_cls
|
||||||
|
else:
|
||||||
|
self.theme_cls = ThemeManager()
|
||||||
|
super(ThemableBehavior, self).__init__(**kwargs)
|
77
kivyblocks/theming_dynamic_text.py
Normal file
77
kivyblocks/theming_dynamic_text.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Two implementations. The first is based on color brightness obtained from:-
|
||||||
|
# https://www.w3.org/TR/AERT#color-contrast
|
||||||
|
# The second is based on relative luminance calculation for sRGB obtained from:-
|
||||||
|
# https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
||||||
|
# and contrast ratio calculation obtained from:-
|
||||||
|
# https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
||||||
|
#
|
||||||
|
# Preliminary testing suggests color brightness more closely matches the
|
||||||
|
# Material Design spec suggested text colors, but the alternative implementation
|
||||||
|
# is both newer and the current 'correct' recommendation, so is included here
|
||||||
|
# as an option.
|
||||||
|
|
||||||
|
|
||||||
|
def _color_brightness(color):
|
||||||
|
# Implementation of color brightness method
|
||||||
|
brightness = color[0] * 299 + color[1] * 587 + color[2] * 114
|
||||||
|
brightness = brightness
|
||||||
|
return brightness
|
||||||
|
|
||||||
|
|
||||||
|
def _black_or_white_by_color_brightness(color):
|
||||||
|
if _color_brightness(color) >= 500:
|
||||||
|
return 'black'
|
||||||
|
else:
|
||||||
|
return 'white'
|
||||||
|
|
||||||
|
|
||||||
|
def _normalized_channel(color):
|
||||||
|
# Implementation of contrast ratio and relative luminance method
|
||||||
|
if color <= 0.03928:
|
||||||
|
return color / 12.92
|
||||||
|
else:
|
||||||
|
return ((color + 0.055) / 1.055) ** 2.4
|
||||||
|
|
||||||
|
|
||||||
|
def _luminance(color):
|
||||||
|
rg = _normalized_channel(color[0])
|
||||||
|
gg = _normalized_channel(color[1])
|
||||||
|
bg = _normalized_channel(color[2])
|
||||||
|
return 0.2126*rg + 0.7152*gg + 0.0722*bg
|
||||||
|
|
||||||
|
|
||||||
|
def _black_or_white_by_contrast_ratio(color):
|
||||||
|
l_color = _luminance(color)
|
||||||
|
l_black = 0.0
|
||||||
|
l_white = 1.0
|
||||||
|
b_contrast = (l_color + 0.05) / (l_black + 0.05)
|
||||||
|
w_contrast = (l_white + 0.05) / (l_color + 0.05)
|
||||||
|
return 'white' if w_contrast >= b_contrast else 'black'
|
||||||
|
|
||||||
|
|
||||||
|
def get_contrast_text_color(color, use_color_brightness=True):
|
||||||
|
if use_color_brightness:
|
||||||
|
contrast_color = _black_or_white_by_color_brightness(color)
|
||||||
|
else:
|
||||||
|
contrast_color = _black_or_white_by_contrast_ratio(color)
|
||||||
|
if contrast_color == 'white':
|
||||||
|
return 1, 1, 1, 1
|
||||||
|
else:
|
||||||
|
return 0, 0, 0, 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from kivy.utils import get_color_from_hex
|
||||||
|
from kivymd.color_definitions import colors, text_colors
|
||||||
|
for c in colors.items():
|
||||||
|
if c[0] in ['Light', 'Dark']:
|
||||||
|
continue
|
||||||
|
print("For the {} color palette:".format(c[0]))
|
||||||
|
for name, hex_color in c[1].items():
|
||||||
|
if hex_color:
|
||||||
|
col = get_color_from_hex(hex_color)
|
||||||
|
col_bri = get_contrast_text_color(col)
|
||||||
|
con_rat = get_contrast_text_color(col, use_color_brightness=False)
|
||||||
|
text_color = text_colors[c[0]][name]
|
||||||
|
print(" The {} hue gives {} using color brightness, {} using contrast ratio, and {} from the MD spec"
|
||||||
|
.format(name, col_bri, con_rat, text_color))
|
245
kivyblocks/tree.py
Normal file
245
kivyblocks/tree.py
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
from kivy.app import App
|
||||||
|
from kivy.graphics import Color, Triangle
|
||||||
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
from kivy.uix.label import Label
|
||||||
|
from kivy.uix.widget import Widget
|
||||||
|
from kivy.uix.button import ButtonBehavior
|
||||||
|
|
||||||
|
from kivyblocks.widgetExt import ScrollWidget
|
||||||
|
from kivyblocks.blocksapp import BlocksApp
|
||||||
|
from kivyblocks.utils import CSize
|
||||||
|
from baseWidget import PressableLabel
|
||||||
|
|
||||||
|
from appPublic.dictObject import DictObject
|
||||||
|
|
||||||
|
class NodeTrigger(ButtonBehavior, Widget):
|
||||||
|
def __init__(self, open_status=False,color=[1,0,0,1]):
|
||||||
|
siz = CSize(1)
|
||||||
|
super().__init__(size_hint=(None,None),size=[siz,siz]) #CSize(1))
|
||||||
|
self.open_status = open_status
|
||||||
|
self.line_color = color
|
||||||
|
self.register_event_type('on_change')
|
||||||
|
self.open_points = [0,self.height, self.width,self.height,self.width/2,0]
|
||||||
|
self.close_points = [0,self.height, 0,0, self.width,self.height/2]
|
||||||
|
self.showOpenStatus()
|
||||||
|
self.bind(size=self.on_size)
|
||||||
|
|
||||||
|
def on_change(self,status):
|
||||||
|
print('new_status=',status)
|
||||||
|
|
||||||
|
def on_size(self,o,v):
|
||||||
|
self.open_points = [0,self.height, self.width,self.height,self.width/2,0]
|
||||||
|
self.close_points = [0,self.height, 0,0, self.width,self.height/2]
|
||||||
|
|
||||||
|
def on_press(self):
|
||||||
|
self.open_status = False if self.open_status else True
|
||||||
|
self.showOpenStatus()
|
||||||
|
self.dispatch('on_change',self.open_status)
|
||||||
|
|
||||||
|
def showOpenStatus(self):
|
||||||
|
points = self.close_points
|
||||||
|
if self.open_status:
|
||||||
|
points = self.open_points
|
||||||
|
self.canvas.clear()
|
||||||
|
with self.canvas:
|
||||||
|
Color(*self.line_color)
|
||||||
|
Triangle(points=points)
|
||||||
|
|
||||||
|
class EmptyBox(Widget):
|
||||||
|
def __init__(self):
|
||||||
|
siz=CSize(1)
|
||||||
|
super().__init__(size_hint=[None,None],size=[siz,siz])
|
||||||
|
|
||||||
|
|
||||||
|
class TreeNode(BoxLayout):
|
||||||
|
def __init__(self,tree:Tree=None,
|
||||||
|
parentNode:TreeNode=None,
|
||||||
|
data:dict):
|
||||||
|
"""
|
||||||
|
base TreeNode
|
||||||
|
data:{
|
||||||
|
if children miss, it is a leaf node,if children is a empty array, is mean need load at it is first expanded.
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
super().__init__(orientation='vertical',size_hint=(None,None))
|
||||||
|
self.treeObj = tree
|
||||||
|
self.data = data
|
||||||
|
self.parentNonde = parentNode
|
||||||
|
self.content = None
|
||||||
|
self.children_open = False
|
||||||
|
self.subNodes = []
|
||||||
|
self.node_box = BoxLayout(orientation='horizontal',size_hint_y=None)
|
||||||
|
self.node_box1 = BoxLayout(orientation='horizontal')
|
||||||
|
n = data.get('children')
|
||||||
|
self.hasChildren_nodes = True if not n is None else False
|
||||||
|
self.children_nodes = n
|
||||||
|
self.children_loaded = False
|
||||||
|
self.hasChildren = data.get('hasChildren')
|
||||||
|
if hasChildren_nodes:
|
||||||
|
self.trigger = NodeTrigger()
|
||||||
|
self.trigger.bind(on_change=self.toggleChildren)
|
||||||
|
self.buildChildrenContainer()
|
||||||
|
else:
|
||||||
|
self.trigger = EmptyBox()
|
||||||
|
self.add_widget(self.node_box)
|
||||||
|
self.node_box.add_widget(self.trigger)
|
||||||
|
self.buildContent()
|
||||||
|
|
||||||
|
def buildChildrenContainer(self):
|
||||||
|
self.node_box1.add_widget(EmptyBox())
|
||||||
|
self.children_box = BoxLayout(orientation='vertical')
|
||||||
|
self.node_box1.add_widget(self.children_box)
|
||||||
|
|
||||||
|
def setMinWidth(self,width):
|
||||||
|
if self.node_box.width < width:
|
||||||
|
self.node_box.width = width
|
||||||
|
if self.node_box1.width < width:
|
||||||
|
self.node_box1.width = width
|
||||||
|
for n in self.subNodes:
|
||||||
|
n.setMinWidth(width)
|
||||||
|
|
||||||
|
def buildContent(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def addContent(self):
|
||||||
|
self.buildContent()
|
||||||
|
self.node_box.add_widget(self.content)
|
||||||
|
self.node_box.height = self.content.height + CSize(1)
|
||||||
|
self.node_box.width = self.trigger.width + self.content.width
|
||||||
|
|
||||||
|
def buildChildren(self):
|
||||||
|
if self.data.children is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.data.children == []:
|
||||||
|
self.treeObj.getUrlData(self.addChildren,self.data)
|
||||||
|
return
|
||||||
|
if len(self.subNodes) == 0:
|
||||||
|
self.addChildren(self.data.children)
|
||||||
|
|
||||||
|
def addChildren(self,children):
|
||||||
|
self.data.children = children
|
||||||
|
for c in children:
|
||||||
|
options['tree'] = self.treeObj
|
||||||
|
options['parentNode'] = self
|
||||||
|
options['data'] = c
|
||||||
|
tn = self.teeeObj.NodeKlass(**options)
|
||||||
|
self.subNodes.append(tn)
|
||||||
|
self.children_box.add_widget(tn)
|
||||||
|
|
||||||
|
def toggleChildren(self,o,v):
|
||||||
|
if v:
|
||||||
|
self.buildChildren()
|
||||||
|
self.add_widget(self.node_box1)
|
||||||
|
self.children_open = True
|
||||||
|
self.height = self.node_box.height + self.node_box1.height
|
||||||
|
else:
|
||||||
|
self.remove_widget(self.node_box1)
|
||||||
|
self.height = self.node_box.height
|
||||||
|
self.children_open = False
|
||||||
|
|
||||||
|
def on_size(self,o,v):
|
||||||
|
print('************treenode on_size', o, v)
|
||||||
|
self.node_box1.height = self.children_box.height
|
||||||
|
|
||||||
|
"""
|
||||||
|
tree options
|
||||||
|
{
|
||||||
|
"url":
|
||||||
|
"params",
|
||||||
|
"bg_color",
|
||||||
|
"color",
|
||||||
|
"checkbox",
|
||||||
|
"multplecheck",
|
||||||
|
"idField",
|
||||||
|
"textFiled",
|
||||||
|
"data" # array of {children:{},...}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
class Tree(ScrollWidget):
|
||||||
|
def __init__(self,**options):
|
||||||
|
super().__init__() #orientation="vertical")
|
||||||
|
self.options = DictObject(**options)
|
||||||
|
self.nodes = []
|
||||||
|
self.initflag = False
|
||||||
|
self.bind(size=self.onSize,pos=self.onSize)
|
||||||
|
|
||||||
|
def onSize(self,o,v=None):
|
||||||
|
if not self.initflag:
|
||||||
|
self.buildTree(self)
|
||||||
|
for n in self.nodes:
|
||||||
|
n.setMinWidth(self.width)
|
||||||
|
|
||||||
|
def setNodeKlass(self,klass):
|
||||||
|
self.NodeKlass = klass
|
||||||
|
|
||||||
|
def getUrlData(self,callback,kv=None):
|
||||||
|
hc = App.get_running_app().hc
|
||||||
|
params = self.options.get(params,{}).copy()
|
||||||
|
if not data is None:
|
||||||
|
for k,v in kv:
|
||||||
|
if k in params.keys():
|
||||||
|
params[k] = v
|
||||||
|
params['id'] = kv[self.options.idField]
|
||||||
|
hc.get(self.url,params=params,callback=callback)
|
||||||
|
|
||||||
|
def buildTree(self,kv=None):
|
||||||
|
if not hasattr(self,'NodeKlass'):
|
||||||
|
self.NodeKlass = TreeNode
|
||||||
|
|
||||||
|
if self.options.url:
|
||||||
|
return self.getUrlData(self.dataLoaded,kv)
|
||||||
|
data = self.options.data
|
||||||
|
self.dataLoaded(data)
|
||||||
|
|
||||||
|
def dataLoaded(self,d):
|
||||||
|
self.data = d
|
||||||
|
self.addNodes()
|
||||||
|
|
||||||
|
def addNodes(self):
|
||||||
|
for c in self.data:
|
||||||
|
options = {}
|
||||||
|
options['tree'] = self
|
||||||
|
options['parentNode'] = None
|
||||||
|
options['data'] = DictObject(c)
|
||||||
|
self.nodes.append(self.NodeKlass(**options))
|
||||||
|
for w in self.nodes:
|
||||||
|
self.add_widget(w)
|
||||||
|
|
||||||
|
class TextTreeNode(TreeNode):
|
||||||
|
def buildContent(self):
|
||||||
|
txt = self.data.get(self.treeObj.options.textField,
|
||||||
|
self.data.get(self.treeObj.options.idField,'defaulttext'))
|
||||||
|
self.content = PressableLabel(text=txt,
|
||||||
|
size_hint=(None,None),
|
||||||
|
font_size=CSize(1),
|
||||||
|
text_size=CSize(len(txt),1),
|
||||||
|
halign='left',
|
||||||
|
height=CSize(2),width=CSize(len(txt)))
|
||||||
|
self.content.bind(on_press=self.onPress)
|
||||||
|
return
|
||||||
|
|
||||||
|
def onPress(self,o,v=None):
|
||||||
|
self.treeObj.select_row(self)
|
||||||
|
|
||||||
|
def selected(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unselected(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TextTree(Tree):
|
||||||
|
def __init__(self,**options):
|
||||||
|
self.NodeKlass = TextTReeNode
|
||||||
|
super().__init__(**options)
|
||||||
|
self.register_event_type('on_press')
|
||||||
|
|
||||||
|
def onPress(self,o,v=None):
|
||||||
|
if self.selectNode:
|
||||||
|
self.selectNode.unselected()
|
||||||
|
self.selectNode = o
|
||||||
|
o.selected()
|
||||||
|
self.dispatch(on_press,o,o.data)
|
||||||
|
|
||||||
|
def on_press(self,o,v):
|
||||||
|
print('TextTree():on_press(),o=',o,'v=',v)
|
Loading…
Reference in New Issue
Block a user