From 4f81e6e8a720cacb35e439aa9cdda4d2b13d457f Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 27 Jan 2022 11:07:46 +0800 Subject: [PATCH] bugfix --- kivyblocks/baseWidget.py | 2 +- kivyblocks/clickable.py | 5 +- kivyblocks/dateinput.py | 4 +- kivyblocks/dg.c | 106 ++++----- kivyblocks/dg.pyx | 4 +- kivyblocks/form.py | 122 ++++++---- kivyblocks/imgs/error.png | Bin 0 -> 23344 bytes kivyblocks/imgs/info.png | Bin 0 -> 25423 bytes kivyblocks/message.py | 50 ++++- kivyblocks/price.py | 47 ++-- kivyblocks/register.py | 1 - kivyblocks/responsivelayout.py | 43 ++-- kivyblocks/toolbar.py | 1 - kivyblocks/uitype.py | 374 ------------------------------- kivyblocks/utils.py | 16 +- kivyblocks/widgetExt/__init__.py | 3 +- kivyblocks/widgetExt/inputext.py | 90 +++----- 17 files changed, 298 insertions(+), 570 deletions(-) create mode 100644 kivyblocks/imgs/error.png create mode 100644 kivyblocks/imgs/info.png delete mode 100644 kivyblocks/uitype.py diff --git a/kivyblocks/baseWidget.py b/kivyblocks/baseWidget.py index ec6502a..def226c 100755 --- a/kivyblocks/baseWidget.py +++ b/kivyblocks/baseWidget.py @@ -63,7 +63,7 @@ from .widgetExt.scrollwidget import ScrollWidget from .widgetExt.binstateimage import BinStateImage from .widgetExt.jsoncodeinput import JsonCodeInput from .widgetExt.inputext import FloatInput,IntegerInput, \ - StrInput,SelectInput, BoolInput, AmountInput + StrInput,SelectInput, BoolInput, Password from .widgetExt.messager import Messager from .charts.bar import Bar from .bgcolorbehavior import BGColorBehavior diff --git a/kivyblocks/clickable.py b/kivyblocks/clickable.py index 17519bf..7d82af4 100644 --- a/kivyblocks/clickable.py +++ b/kivyblocks/clickable.py @@ -13,7 +13,7 @@ from kivyblocks.bgcolorbehavior import BGColorBehavior from kivyblocks.utils import CSize, SUPER, blockImage from kivyblocks.baseWidget import Box, Text from kivyblocks.widget_css import WidgetCSS -from kivyblocks.uitype import view_register, input_register, get_value +from .uitype.factory import UiFactory, get_value class TinyText(Text): def __init__(self, **kw): @@ -286,8 +286,7 @@ def build_checkbox(desc, rec=None): x = SingleCheckBox(select_state=v) return x -view_register('checkbox', build_checkbox) -input_register('checkbox', build_checkbox) +UiFactory.register('checkbox', build_checkbox, build_checkbox) r = Factory.register r('TinyText', TinyText) diff --git a/kivyblocks/dateinput.py b/kivyblocks/dateinput.py index 6c2dfff..db3ca51 100644 --- a/kivyblocks/dateinput.py +++ b/kivyblocks/dateinput.py @@ -140,7 +140,7 @@ class DateInput(HBox): self.datetype = 'yyyy-mm-dd' y = int(datestr[:4]) m = int(datestr[5:7]) - d = int(datestr[9:10]) + d = int(datestr[8:10]) return y, m, d def ymd2str(self, y, m, d): @@ -175,6 +175,8 @@ class DateInput(HBox): return None def setValue(self, datestr): + if datestr == '': + datestr = curDateString() self.old_value = datestr y, m, d = self.str2ymd(datestr) self.yw.setValue(y) diff --git a/kivyblocks/dg.c b/kivyblocks/dg.c index 35deb30..49ea73e 100644 --- a/kivyblocks/dg.c +++ b/kivyblocks/dg.c @@ -1607,7 +1607,6 @@ static const char __pyx_k_row_id[] = "row_id"; static const char __pyx_k_select[] = "select"; static const char __pyx_k_tailer[] = "tailer"; static const char __pyx_k_target[] = "target"; -static const char __pyx_k_uitype[] = "uitype"; static const char __pyx_k_update[] = "update"; static const char __pyx_k_valign[] = "valign"; static const char __pyx_k_Factory[] = "Factory"; @@ -1648,6 +1647,7 @@ static const char __pyx_k_setWidth[] = "setWidth"; static const char __pyx_k_vertical[] = "vertical"; static const char __pyx_k_BoxLayout[] = "BoxLayout"; static const char __pyx_k_Rectangle[] = "Rectangle"; +static const char __pyx_k_UiFactory[] = "UiFactory"; static const char __pyx_k_WidgetCSS[] = "WidgetCSS"; static const char __pyx_k_bar_width[] = "bar_width"; static const char __pyx_k_cell_type[] = "cell_type"; @@ -1759,6 +1759,7 @@ static const char __pyx_k_on_scroll_stop[] = "on_scroll_stop"; static const char __pyx_k_row_normal_css[] = "row_normal_css"; static const char __pyx_k_selected_rowid[] = "selected_rowid"; static const char __pyx_k_tailer_widgets[] = "tailer_widgets"; +static const char __pyx_k_uitype_factory[] = "uitype.factory"; static const char __pyx_k_BGColorBehavior[] = "BGColorBehavior"; static const char __pyx_k_Body_delRowById[] = "Body.delRowById"; static const char __pyx_k_Body_getRowData[] = "Body.getRowData"; @@ -1935,6 +1936,7 @@ static PyObject *__pyx_n_s_StringProperty; static PyObject *__pyx_n_s_Text; static PyObject *__pyx_n_s_TimeCost; static PyObject *__pyx_n_s_Toolbar; +static PyObject *__pyx_n_s_UiFactory; static PyObject *__pyx_n_s_VBox; static PyObject *__pyx_n_s_WidgetCSS; static PyObject *__pyx_n_s_WidgetReady; @@ -2171,7 +2173,7 @@ static PyObject *__pyx_n_s_time; static PyObject *__pyx_n_s_toolbar; static PyObject *__pyx_n_s_toolbar_2; static PyObject *__pyx_n_s_txt; -static PyObject *__pyx_n_s_uitype; +static PyObject *__pyx_n_s_uitype_factory; static PyObject *__pyx_n_s_ujson; static PyObject *__pyx_n_s_unselect_row; static PyObject *__pyx_n_s_unselected; @@ -2937,7 +2939,7 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject * self.cell_type = 'header' * else: * self.cell_type = 'data' # <<<<<<<<<<<<<< - * bl = build_view_widget(desc,self.row.row_data) + * bl = UiFactory.build_view_widget(desc,self.row.row_data) * self.colume_name = desc['name'] */ /*else*/ { @@ -2946,43 +2948,46 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject /* "kivyblocks/dg.pyx":72 * else: * self.cell_type = 'data' - * bl = build_view_widget(desc,self.row.row_data) # <<<<<<<<<<<<<< + * bl = UiFactory.build_view_widget(desc,self.row.row_data) # <<<<<<<<<<<<<< * self.colume_name = desc['name'] * if bl: */ - __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_build_view_widget); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 72, __pyx_L1_error) + __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_UiFactory); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 72, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); - __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_row); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 72, __pyx_L1_error) + __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_build_view_widget); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 72, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_row_data); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 72, __pyx_L1_error) + __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; + __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_row); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 72, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_4); + __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_row_data); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 72, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = NULL; + __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; + __pyx_t_4 = NULL; __pyx_t_7 = 0; - if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_4))) { - __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_4); - if (likely(__pyx_t_1)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4); - __Pyx_INCREF(__pyx_t_1); + if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_1))) { + __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_1); + if (likely(__pyx_t_4)) { + PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_1); + __Pyx_INCREF(__pyx_t_4); __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_4, function); + __Pyx_DECREF_SET(__pyx_t_1, function); __pyx_t_7 = 1; } } #if CYTHON_FAST_PYCALL - if (PyFunction_Check(__pyx_t_4)) { - PyObject *__pyx_temp[3] = {__pyx_t_1, __pyx_v_desc, __pyx_t_2}; - __pyx_t_5 = __Pyx_PyFunction_FastCall(__pyx_t_4, __pyx_temp+1-__pyx_t_7, 2+__pyx_t_7); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; + if (PyFunction_Check(__pyx_t_1)) { + PyObject *__pyx_temp[3] = {__pyx_t_4, __pyx_v_desc, __pyx_t_2}; + __pyx_t_5 = __Pyx_PyFunction_FastCall(__pyx_t_1, __pyx_temp+1-__pyx_t_7, 2+__pyx_t_7); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) + __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; } else #endif #if CYTHON_FAST_PYCCALL - if (__Pyx_PyFastCFunction_Check(__pyx_t_4)) { - PyObject *__pyx_temp[3] = {__pyx_t_1, __pyx_v_desc, __pyx_t_2}; - __pyx_t_5 = __Pyx_PyCFunction_FastCall(__pyx_t_4, __pyx_temp+1-__pyx_t_7, 2+__pyx_t_7); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; + if (__Pyx_PyFastCFunction_Check(__pyx_t_1)) { + PyObject *__pyx_temp[3] = {__pyx_t_4, __pyx_v_desc, __pyx_t_2}; + __pyx_t_5 = __Pyx_PyCFunction_FastCall(__pyx_t_1, __pyx_temp+1-__pyx_t_7, 2+__pyx_t_7); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) + __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; } else @@ -2990,8 +2995,8 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject { __pyx_t_3 = PyTuple_New(2+__pyx_t_7); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 72, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); - if (__pyx_t_1) { - __Pyx_GIVEREF(__pyx_t_1); PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1); __pyx_t_1 = NULL; + if (__pyx_t_4) { + __Pyx_GIVEREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_4); __pyx_t_4 = NULL; } __Pyx_INCREF(__pyx_v_desc); __Pyx_GIVEREF(__pyx_v_desc); @@ -2999,11 +3004,11 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject __Pyx_GIVEREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_3, 1+__pyx_t_7, __pyx_t_2); __pyx_t_2 = 0; - __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_3, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) + __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_3, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 72, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; } - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; + __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_v_bl = __pyx_t_5; __pyx_t_5 = 0; } @@ -3011,7 +3016,7 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject /* "kivyblocks/dg.pyx":73 * self.cell_type = 'data' - * bl = build_view_widget(desc,self.row.row_data) + * bl = UiFactory.build_view_widget(desc,self.row.row_data) * self.colume_name = desc['name'] # <<<<<<<<<<<<<< * if bl: * self.add_widget(bl) @@ -3022,7 +3027,7 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; /* "kivyblocks/dg.pyx":74 - * bl = build_view_widget(desc,self.row.row_data) + * bl = UiFactory.build_view_widget(desc,self.row.row_data) * self.colume_name = desc['name'] * if bl: # <<<<<<<<<<<<<< * self.add_widget(bl) @@ -3038,27 +3043,27 @@ static PyObject *__pyx_pf_10kivyblocks_2dg_4Cell___init__(CYTHON_UNUSED PyObject * * class Row(BoxLayout): */ - __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_add_widget); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 75, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); + __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_add_widget); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 75, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_1); __pyx_t_3 = NULL; - if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_4))) { - __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_4); + if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_1))) { + __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_1); if (likely(__pyx_t_3)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4); + PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_1); __Pyx_INCREF(__pyx_t_3); __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_4, function); + __Pyx_DECREF_SET(__pyx_t_1, function); } } - __pyx_t_5 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_4, __pyx_t_3, __pyx_v_bl) : __Pyx_PyObject_CallOneArg(__pyx_t_4, __pyx_v_bl); + __pyx_t_5 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_1, __pyx_t_3, __pyx_v_bl) : __Pyx_PyObject_CallOneArg(__pyx_t_1, __pyx_v_bl); __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 75, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; + __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; /* "kivyblocks/dg.pyx":74 - * bl = build_view_widget(desc,self.row.row_data) + * bl = UiFactory.build_view_widget(desc,self.row.row_data) * self.colume_name = desc['name'] * if bl: # <<<<<<<<<<<<<< * self.add_widget(bl) @@ -17656,6 +17661,7 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_n_s_Text, __pyx_k_Text, sizeof(__pyx_k_Text), 0, 0, 1, 1}, {&__pyx_n_s_TimeCost, __pyx_k_TimeCost, sizeof(__pyx_k_TimeCost), 0, 0, 1, 1}, {&__pyx_n_s_Toolbar, __pyx_k_Toolbar, sizeof(__pyx_k_Toolbar), 0, 0, 1, 1}, + {&__pyx_n_s_UiFactory, __pyx_k_UiFactory, sizeof(__pyx_k_UiFactory), 0, 0, 1, 1}, {&__pyx_n_s_VBox, __pyx_k_VBox, sizeof(__pyx_k_VBox), 0, 0, 1, 1}, {&__pyx_n_s_WidgetCSS, __pyx_k_WidgetCSS, sizeof(__pyx_k_WidgetCSS), 0, 0, 1, 1}, {&__pyx_n_s_WidgetReady, __pyx_k_WidgetReady, sizeof(__pyx_k_WidgetReady), 0, 0, 1, 1}, @@ -17892,7 +17898,7 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_n_s_toolbar, __pyx_k_toolbar, sizeof(__pyx_k_toolbar), 0, 0, 1, 1}, {&__pyx_n_s_toolbar_2, __pyx_k_toolbar_2, sizeof(__pyx_k_toolbar_2), 0, 0, 1, 1}, {&__pyx_n_s_txt, __pyx_k_txt, sizeof(__pyx_k_txt), 0, 0, 1, 1}, - {&__pyx_n_s_uitype, __pyx_k_uitype, sizeof(__pyx_k_uitype), 0, 0, 1, 1}, + {&__pyx_n_s_uitype_factory, __pyx_k_uitype_factory, sizeof(__pyx_k_uitype_factory), 0, 0, 1, 1}, {&__pyx_n_s_ujson, __pyx_k_ujson, sizeof(__pyx_k_ujson), 0, 0, 1, 1}, {&__pyx_n_s_unselect_row, __pyx_k_unselect_row, sizeof(__pyx_k_unselect_row), 0, 0, 1, 1}, {&__pyx_n_s_unselected, __pyx_k_unselected, sizeof(__pyx_k_unselected), 0, 0, 1, 1}, @@ -19608,7 +19614,7 @@ if (!__Pyx_RefNanny) { * from .toolbar import Toolbar * from .bgcolorbehavior import BGColorBehavior # <<<<<<<<<<<<<< * from .widget_css import WidgetCSS - * from .uitype import build_view_widget + * from .uitype.factory import UiFactory */ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 31, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); @@ -19628,7 +19634,7 @@ if (!__Pyx_RefNanny) { * from .toolbar import Toolbar * from .bgcolorbehavior import BGColorBehavior * from .widget_css import WidgetCSS # <<<<<<<<<<<<<< - * from .uitype import build_view_widget + * from .uitype.factory import UiFactory * */ __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 32, __pyx_L1_error) @@ -19648,26 +19654,26 @@ if (!__Pyx_RefNanny) { /* "kivyblocks/dg.pyx":33 * from .bgcolorbehavior import BGColorBehavior * from .widget_css import WidgetCSS - * from .uitype import build_view_widget # <<<<<<<<<<<<<< + * from .uitype.factory import UiFactory # <<<<<<<<<<<<<< * * class BLabel(ButtonBehavior, Text): */ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 33, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); - __Pyx_INCREF(__pyx_n_s_build_view_widget); - __Pyx_GIVEREF(__pyx_n_s_build_view_widget); - PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_build_view_widget); - __pyx_t_2 = __Pyx_Import(__pyx_n_s_uitype, __pyx_t_1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 33, __pyx_L1_error) + __Pyx_INCREF(__pyx_n_s_UiFactory); + __Pyx_GIVEREF(__pyx_n_s_UiFactory); + PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_UiFactory); + __pyx_t_2 = __Pyx_Import(__pyx_n_s_uitype_factory, __pyx_t_1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 33, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_build_view_widget); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 33, __pyx_L1_error) + __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_UiFactory); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 33, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_build_view_widget, __pyx_t_1) < 0) __PYX_ERR(0, 33, __pyx_L1_error) + if (PyDict_SetItem(__pyx_d, __pyx_n_s_UiFactory, __pyx_t_1) < 0) __PYX_ERR(0, 33, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; /* "kivyblocks/dg.pyx":35 - * from .uitype import build_view_widget + * from .uitype.factory import UiFactory * * class BLabel(ButtonBehavior, Text): # <<<<<<<<<<<<<< * def __init__(self, **kw): @@ -19703,7 +19709,7 @@ if (!__Pyx_RefNanny) { __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; /* "kivyblocks/dg.pyx":35 - * from .uitype import build_view_widget + * from .uitype.factory import UiFactory * * class BLabel(ButtonBehavior, Text): # <<<<<<<<<<<<<< * def __init__(self, **kw): diff --git a/kivyblocks/dg.pyx b/kivyblocks/dg.pyx index 25ed58c..7c5bf90 100644 --- a/kivyblocks/dg.pyx +++ b/kivyblocks/dg.pyx @@ -30,7 +30,7 @@ from .ready import WidgetReady from .toolbar import Toolbar from .bgcolorbehavior import BGColorBehavior from .widget_css import WidgetCSS -from .uitype import build_view_widget +from .uitype.factory import UiFactory class BLabel(ButtonBehavior, Text): def __init__(self, **kw): @@ -69,7 +69,7 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout): self.cell_type = 'header' else: self.cell_type = 'data' - bl = build_view_widget(desc,self.row.row_data) + bl = UiFactory.build_view_widget(desc,self.row.row_data) self.colume_name = desc['name'] if bl: self.add_widget(bl) diff --git a/kivyblocks/form.py b/kivyblocks/form.py index 5bca9ed..464e476 100644 --- a/kivyblocks/form.py +++ b/kivyblocks/form.py @@ -1,9 +1,11 @@ +from math import floor from kivy.factory import Factory from kivy.properties import NumericProperty, StringProperty, \ DictProperty, ListProperty, OptionProperty, BooleanProperty from kivy.logger import Logger from kivy.uix.textinput import TextInput from kivy.uix.boxlayout import BoxLayout +from kivy.uix.stacklayout import StackLayout from kivy.core.window import Window from kivy.graphics import Color from .responsivelayout import VResponsiveLayout @@ -16,7 +18,7 @@ from .dataloader import DataGraber from .ready import WidgetReady from .widget_css import WidgetCSS from .dateinput import DateInput -from .uitype import get_input_builder +from .uitype.factory import UiFactory # import get_input_builder """ form options @@ -58,7 +60,6 @@ class InputBox(BoxLayout): self.options = options self.uitype = options.get('uitype',options.get('datatype','str')) self.size_hint = (None, None) - width = self.form.inputwidth height = self.form.inputheight if self.uitype == 'text': if not options.get('height'): @@ -74,16 +75,21 @@ class InputBox(BoxLayout): else: kwargs['size_hint_y'] = None kwargs['height'] = CSize(height) - if width <= 1: - kwargs['size_hint_x'] = width + if self.form.input_width <= 1: + kwargs['size_hint_x'] = self.form.input_width else: kwargs['size_hint_x'] = None - kwargs['width'] = CSize(width) + kwargs['width'] = self.form.input_width super().__init__(**kwargs) self.initflag = False - self.bind(size=self.setSize, - pos=self.setSize) self.register_event_type("on_datainput") + self.init() + + def focus_act(self, *args): + show_widget_info(self,'test') + show_widget_info(self.labeltext,'test') + show_widget_info(self.input_widget,'test') + def on_size(self, *args): if self.input_widget is None: @@ -97,6 +103,10 @@ class InputBox(BoxLayout): self.input_widget.size_hint_x = None self.input_widget.width = self.width - self.labeltext.width - CSize(1) + show_widget_info(self,'test') + show_widget_info(self.labeltext,'test') + show_widget_info(self.input_widget,'test') + def on_datainput(self,o,v=None): print('on_datainput fired ...',o,v) @@ -104,44 +114,42 @@ class InputBox(BoxLayout): if self.initflag: return i18n = I18n() - opts = { - "orientation":"horizontal", - "size_hint_y":None, - "height":CSize(self.form.inputheight) - } - if self.form.inputwidth > 1: - opts['size_hint_x'] = None - opts['width'] = CSize(self.form.inputwidth) - else: - opts['size_hint_x'] = self.form.inputwidth - - bl = BoxLayout(**opts) - self.add_widget(bl) label = self.options.get('label',self.options.get('name')) kwargs = { "i18n":True, "otext":label, "font_size":CSize(1), } - if self.form.labelwidth < 1: + if self.form.labelwidth <= 1: kwargs['size_hint_x'] = self.form.labelwidth else: kwargs['size_hint_x'] = None kwargs['width'] = CSize(self.form.labelwidth) self.labeltext = Text(**kwargs) - bl.add_widget(self.labeltext) + self.add_widget(self.labeltext) if self.options.get('required',False): star = Label(text='*', color=(1,0,0,1), size_hint_x=None, width=CSize(1)) - bl.add_widget(star) + self.add_widget(star) options = self.options.copy() options['hint_text'] = i18n(self.options.get('hint_text')) - f = get_input_builder(self.uitype) - self.input_widget = f(options) + options['size_hint_y'] = None + options['height'] = self.height + print('options=', options) + self.input_widget = UiFactory.build_input_widget(options) + + print('inputw.height=',self.input_widget.height) + try: + self.input_widget.bind(focus=self.focus_act) + except: + pass + self.input_widget.height = self.height + self.input_widget.size_hint_y = None + if self.form.labelwidth < 1: self.input_widget.size_hint_x = 1 - self.form.labelwidth else: @@ -186,9 +194,6 @@ class InputBox(BoxLayout): if self.old_value != o.text: self.dispatch('on_datainput',o.text) - def setSize(self,o,v=None): - self.init() - def setValue(self,v): self.input_widget.setValue(v) @@ -225,7 +230,7 @@ def defaultToolbar(): } class Form(WidgetCSS, WidgetReady, BoxLayout): - cols = NumericProperty(1) + cols = NumericProperty(None) csscls=StringProperty('formcss') input_css=StringProperty('formfieldcss') inputwidth=NumericProperty(1) @@ -239,22 +244,60 @@ class Form(WidgetCSS, WidgetReady, BoxLayout): submit=DictProperty(None) clear=DictProperty(None) notoolbar=BooleanProperty(False) - def __init__(self, - **options): + def __init__(self, **options): + self.cols = 1 if self.toolbar_at in ['top','bottom']: options['orientation'] = 'vertical' else: options['orientation'] = 'horizontal' SUPER(Form, self, options) - if self.inputwidth <= 1: - self.cols = 1 - self.options_cols = self.cols - if isHandHold() and Window.width < Window.height: - self.cols = 1 - self.options = options + self.setup_cols_and_inputwidth() self.init() self.register_event_type('on_submit') + def setup_cols_and_inputwidth(self): + if isHandHold() and Window.width < Window.height: + self._cols = 1 + self.input_width = 1 + print('1-inputwidth=', self.inputwidth, + 'input_width=',self.input_width, + 'CSize(1)=', CSize(1)) + return + if self.cols is None and self.inputwidth is None: + self.input_width = 1 + self._cols = 1 + print('2-inputwidth=', self.inputwidth, + 'input_width=',self.input_width, + 'CSize(1)=', CSize(1)) + return + if self.inputwidth > 1: + self.input_width = CSize(self.inputwidth) + self._cols = -1 # auto calculate + print('3-inputwidth=', self.inputwidth, + 'input_width=',self.input_width, + 'CSize(1)=', CSize(1), CSize(self.inputwidth)) + return + if self.cols is not None: + self._cols = self.cols + if self.inputwidth is None: + self.input_width = -1 + else: + self.input_width = CSize(self.inputwidth) + print('4-inputwidth=', self.inputwidth, + 'input_width=',self.input_width, + 'CSize(1)=', CSize(1)) + return + self.input_width = -1 + self._cols = 1 + print('5-inputwidth=', self.inputwidth, + 'input_width=',self.input_width, + 'CSize(1)=', CSize(1), CSize(self.inputwidth)) + + def set_grid_attrs(self): + if self._cols == 1 and self.input_width <= 1: + self.input_width = self.width + self.fsc.box_width = self.input_width + def on_size(self, *args): if not hasattr(self,'fsc'): return @@ -269,7 +312,7 @@ class Form(WidgetCSS, WidgetReady, BoxLayout): self.fsc.height = self.height else: self.fsc.width = self.width - self.fsc.org_box_width = self.width / self.options_cols + self.set_grid_attrs() def init(self): if not self.notoolbar: @@ -303,8 +346,7 @@ class Form(WidgetCSS, WidgetReady, BoxLayout): self.toolbar_w = Factory.Toolbar(**desc) self.fsc = VResponsiveLayout( - self.inputwidth, - self.cols, + box_width = self.input_width, size_hint=(1,1) ) diff --git a/kivyblocks/imgs/error.png b/kivyblocks/imgs/error.png new file mode 100644 index 0000000000000000000000000000000000000000..9091bb4121dfacc8972aa68280d51c0a3d1baeaa GIT binary patch literal 23344 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 D7UU*% literal 0 HcmV?d00001 diff --git a/kivyblocks/imgs/info.png b/kivyblocks/imgs/info.png new file mode 100644 index 0000000000000000000000000000000000000000..eba41f48e5695d4d6950f4d72cfc3da74425c3d0 GIT binary patch literal 25423 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 self.old_price: + elif self.price > self.compare_price: self.color = self.inc_color - elif self.price < self.old_price: + elif self.price < self.compare_price: self.color = self.dec_color else: self.color = nor_color - self.old_price = self.price def getValue(self): return self.price @@ -40,14 +43,26 @@ class PriceView(Label): def setValue(self, v): self.price = v + @classmethod + def builder(desc, rec=None): + v = get_value(desc, rec=rec) + return PriceView(price=v, + font_size=CSize(1), + halign='right', + valign='middle' + ) + +class PriceInput(FloatInput): + @classmethod + def builder(self, desc, rec=None): + v = get_value(desc, rec=rec) + kw = desc.get('uiparams', {}) + w = PriceInput(**kw) + if v is not None: + w.setValue(v) + return w + Factory.register('PriceView', PriceView) -def build_view_price_widget(desc, rec=None): - v = get_value(desc, rec=rec) - return Factory.PriceView(price=v, - font_size=CSize(1), - halign='right', - valign='middle' - ) -view_register('price', build_view_price_widget) +UiFactory.register('price', PriceInput.builder, PriceView.builder) diff --git a/kivyblocks/register.py b/kivyblocks/register.py index ca25ea3..2c65fa0 100644 --- a/kivyblocks/register.py +++ b/kivyblocks/register.py @@ -97,7 +97,6 @@ r('IntegerInput',IntegerInput) r('StrInput',StrInput) r('SelectInput',SelectInput) r('BoolInput',BoolInput) -r('AmountInput',AmountInput) r('Messager',Messager) r('Bar',Bar) r('LoginForm',LoginForm) diff --git a/kivyblocks/responsivelayout.py b/kivyblocks/responsivelayout.py index 4872408..6c79724 100644 --- a/kivyblocks/responsivelayout.py +++ b/kivyblocks/responsivelayout.py @@ -1,35 +1,42 @@ +from math import floor +from kivy.properties import NumericProperty from kivy.utils import platform from kivy.uix.scrollview import ScrollView from kivy.uix.gridlayout import GridLayout from kivy.graphics import Color, Ellipse,Rectangle from kivy.clock import Clock -from .utils import CSize, isHandHold +from .utils import CSize, isHandHold, show_widget_info class VResponsiveLayout(ScrollView): - def __init__(self,box_width,cols, **kw): - self.org_box_width = box_width - self.org_cols = cols - self.box_width = box_width - self.box_cols = cols + box_width = NumericProperty(1) + def __init__(self, **kw): + self._inner = None super(VResponsiveLayout, self).__init__(**kw) self.options = kw - self._inner = GridLayout(cols=self.org_cols, padding=2, + self._inner = GridLayout(cols=1, padding=2, spacing=2,size_hint=(1,None)) + self._inner.col_force_default = True + self._inner.col_default_width = self.box_width super(VResponsiveLayout,self).add_widget(self._inner) self._inner.bind( minimum_height=self._inner.setter('height')) - self.sizeChangedTask = None + self.setCols() self.bind(pos=self.setCols,size=self.setCols) + def on_box_width(self, *args): + if not self._inner: + return + self._inner.col_default_width = self.box_width + for w in self._inner.children: + w.size_hint_x = None + w.width = self.box_width + self.setCols() + def on_orientation(self,o): self.setCols() def add_widget(self,widget,**kw): - width = self.box_width - if hasattr(widget, 'cols'): - width = widget.cols * self.box_width - widget.width = width a = self._inner.add_widget(widget,**kw) return a @@ -41,13 +48,13 @@ class VResponsiveLayout(ScrollView): return a def setCols(self,*args): - cols = round(self.width / self.org_box_width) + cols = floor(self.width / self.box_width) if cols < 1: cols = 1 - if isHandHold() and self.width < self.height and self.org_cols >= 2: - cols = 2 - box_width = self.width / cols - 2 self._inner.cols = cols - for w in self._inner.children: - w.width = box_width + #print(self.width, self._inner.width, self.box_width, cols, cols * self.box_width) + #for c in self.children[:2]: + # show_widget_info(c) + # for c1 in c.children: + # show_widget_info(c1) diff --git a/kivyblocks/toolbar.py b/kivyblocks/toolbar.py index 208d1cf..1a90bcc 100644 --- a/kivyblocks/toolbar.py +++ b/kivyblocks/toolbar.py @@ -155,7 +155,6 @@ class Toolbar(ScrollPanel): w.bind(on_press=self.tool_press) w.bind(minimum_width=w.setter('width')) w.bind(minimum_height=w.setter('height')) - print(w, 'children=', len(w.children)) def delete_tool(self, o): w, t = o.data diff --git a/kivyblocks/uitype.py b/kivyblocks/uitype.py deleted file mode 100644 index 96287a9..0000000 --- a/kivyblocks/uitype.py +++ /dev/null @@ -1,374 +0,0 @@ -#uitype.py -""" -each ui type need to provides a view widget and a input widget -to auto construct the form widget and record show widget. - -we use a register machinans to maintian the ui type information to build the view widget and input widget -""" - -import ujson as json -from kivy.factory import Factory -from kivy.properties import NumericProperty, StringProperty, \ - BooleanProperty -from kivy.uix.label import Label - -from appPublic.myTE import string_template_render - -from kivyblocks.utils import CSize -from .baseWidget import WrapText, Text -from .dateinput import DateInput -from .widgetExt.inputext import FloatInput,IntegerInput, \ - StrInput,SelectInput, BoolInput, AmountInput, Password - -class StringView(Label): - def __init__(self, **kw): - super(StringView, self).__init__(**kw) - self.wrap = True - self.halign = 'left' - self.valign = 'middle' - - def getValue(self): - return self.text - - def setValue(self, v): - self.text = str(v) - -class PasswordView(StringView): - def __init__(self, text='*', **kw): - super(PasswordView, self).__init__(text=text, **kw) - - def getValue(self): - return None - - def setValue(self, v): - pass - -class TextView(WrapText): - def getValue(self): - return self.text - - def setValue(self, v): - self.text = v - -view_widget_builders = { -} - -input_widget_builders = {} - -def view_register(uitype, f): - view_widget_builders.update({uitype:f}) - -def input_register(uitype, f): - input_widget_builders.update({uitype:f}) - -def get_input_builder(uitype): - return input_widget_builders.get(uitype, \ - input_widget_builders.get('str', None)) - -def get_view_builder(uitype): - return view_widget_builders.get(uitype, \ - view_widget_builders.get('str', None)) - -def build_view_widget(desc, rec = None): - viewer = desc.get('viewer') - if viewer: - if isinstance(viewer,dict): - viewer = json.dumps(viewer) - rendered = string_template_render(viewer, rec) - dic = json.loads(rendered) - if dic is None: - return None - w = Factory.Blocks().widgetBuild(dic) - return w - - f = get_view_builder(desc.get('uitype')) - return f(desc, rec) - -def build_input_widget(desc, rec=None): - f = get_input_builder(desc.get('uitype')) - return f(desc, rec) - -def get_value(desc, rec=None): - if rec: - return rec.get(desc['name']) - return desc.get('value', desc.get('defaultvalue', '')) - -def build_view_str_widget(desc, rec=None): - txt = get_value(desc, rec=rec) - return StringView(text=str(txt) if txt else '', - font_size=CSize(1), - halign='left', - valign='middle') - -view_register('str', build_view_str_widget) -view_register('date', build_view_str_widget) -view_register('time', build_view_str_widget) -view_register('timestamp', build_view_str_widget) - -def build_view_text_widget(desc, rec=None): - txt = get_value(desc, rec=rec) - return TextView(text=str(txt) if txt else '', - font_size=CSize(1), - halign='left', - valign='middle') - -view_register('text', build_view_text_widget) - -def build_view_passwd_widget(desc, rec=None): - return PasswordView(text='*', - font_size=CSize(1), - halign='left', - valign='middle') - -view_register('password', build_view_passwd_widget) - -class BoolView(Label): - value = BooleanProperty(False) - def __init__(self, text='', **kw): - super(BoolView, self).__init__(text='', **kw) - self.wrap = True - self.halign = 'center' - self.valign = 'middle' - - def on_value(self, *args): - if self.value: - self.text = 'true' - else: - self.text = 'false' - - def getValue(self): - return self.value - - def setValue(self, v): - self.value = v - -def build_view_bool_widget(desc, rec=None): - v = get_value(desc, rec) - if v is not None: - return BoolView(value=v, - font_size=CSize(1), - halign='left', - valign='middle') - - default_value = desc.get('nullvalue',False) - return BoolView(value=default_Value, - font_size=CSize(1), - halign='left', - valign='middle') - -view_register('bool', build_view_bool_widget) - -class IntView(Text): - value = NumericProperty(None) - nullstr = StringProperty('') - def __init__(self, **kw): - super(IntView, self).__init__(text='', **kw) - self.wrap = True - self.halign = 'right' - self.valign = 'middle' - - def on_value(self, *args): - if self.value is not None: - self.text = str(v) - else: - self.text = self.nullstr - - def setValue(self, v): - self.value = v - - def getValue(self): - return self.value - -def build_view_int_widget(desc, rec=None): - v = get_value(desc, rec=rec) - return IntValue(value=v, - nullstr=desc.get('nullstr',''), - font_size=CSize(1), - halign='right', - valign='middle' - ) - -view_register('int', build_view_int_widget) - -class FloatView(Text): - value = NumericProperty(None) - nullstr = StringProperty('') - dec = NumericProperty(2) - def __init__(self, **kw): - super(FloatView, self).__init__(text=self.nullstr, **kw) - self.wrap = True - self.halign = 'right' - self.valign = 'middle' - - def on_value(self, *args): - if self.value is None: - self.text = self.bullstr - f = '%%.0%df' % self.dec - self.text = f % self.value - - def getValue(self): - return self.value - - def setValue(self, v): - self.value = v - -def build_view_float_widget(desc, rec=None): - v = get_value(desc, rec=rec) - return FloatView(value=v, - dec=desc.get('dec', 2), - nullstr=desc.get('nullstr',''), - font_size=CSize(1), - halign='right', - valign='middle' - ) - -view_register('float', build_view_float_widget) - -def build_view_code_widget(desc, rec=None): - tf = desc.get('textField','text') - vf = desc.get('valueField','value') - v = rec.get(tf,rec.get(vf, ' ')) - return Text(text=v, - i18n=True, - font_size = CSize(1), - wrap=True, - halign='center', - valign='middle' - ) - -view_register('code', build_view_code_widget) - -class AmountView(FloatView): - def on_value(self, *args): - if self.value is None: - self.text = self.nullstr - else: - self.text = self.amount_str() - - def amount_str(self): - f = '%%.0%df' % self.dec - s = f % self.value - parts = s.split('.') - p_int = parts[0] - subs = [] - if p_int[0] == '-': - subs.append('-') - p_int = p_int[1:] - l = [i for i in p_int] - l.reverse() - k = [] - for i,c in enumerate(l): - if i and (i) % 3 == 0: - k.append(',') - k.append(c) - k.reverse() - subs.append(''.join(k)) - if len(parts) > 1: - subs.append(f'.{parts[1]}') - return ''.join(subs) - -def build_view_amount_widget(desc, rec=None): - v = get_value(desc, rec=rec) - defaultvalue = desc.get('nullstr', '') - return Amount(value=v, - dec=desc.get('dec', 2), - nullstr=defaultvale, - font_size=CSize(1), - halign='right', - valign='middle' - ) - -view_register('amount', build_view_amount_widget) - -def build_input_str_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = StrInput(**kw) - if v: - w.setValue(v) - return w - -input_register('str', build_input_str_widget) - -def build_input_text_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - kw['multiline'] = True - w = StrInput(**kw) - if v: - w.setValue(v) - return w - -input_register('text', build_input_text_widget) - -def build_input_int_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = IntegerInput(**kw) - if v: - w.setValue(v) - return w - -input_register('int', build_input_int_widget) -input_register('time', build_input_int_widget) - -def build_input_float_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = FloatInput(**kw) - if v: - w.setValue(v) - return w - -input_register('float', build_input_float_widget) - -def build_input_amount_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = AmountInput(**kw) - if v: - w.setValue(v) - return w - -input_register('amount', build_input_amount_widget) - -def build_input_date_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = DateInput(**kw) - if v: - w.setValue(v) - return w - -input_register('date', build_input_date_widget) - -def build_input_bool_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = BoolInput(**kw) - if v: - w.setValue(v) - return w - -input_register('bool', build_input_bool_widget) - -def build_input_code_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = SelectInput(**kw) - if v: - w.setValue(v) - return w - -input_register('code', build_input_code_widget) - -def build_input_password_widget(desc, rec=None): - v = get_value(desc, rec=rec) - kw = desc.get('uiparams', {}) - w = Password(**kw) - if v: - w.setValue(v) - return w - -input_register('password', build_input_password_widget) - diff --git a/kivyblocks/utils.py b/kivyblocks/utils.py index 361741f..8f45d4a 100644 --- a/kivyblocks/utils.py +++ b/kivyblocks/utils.py @@ -74,6 +74,20 @@ def loading(parent): view.open() return view +def set_widget_width(self, width): + if width <= 1: + self.size_hint_x = width + else: + self.size_hint_x = None + self.width = width + +def set_widget_height(self, height): + if height <= 1: + self.size_hint_y = height + else: + self.size_hint_y = None + self.height = height + def setSizeOptions(desc,kw): """ desc's width, and height to setup a widget's size options @@ -217,5 +231,5 @@ def absurl(url,parent): def show_widget_info(w, tag='DEBUG'): id = getattr(w, 'widget_id', 'null') - msg=f"""{tag}:size_hint={w.size_hint},size={w.size},pos={w.pos},widget_id={id}""" + msg=f"""{tag}:size_hint={w.size_hint},size={w.size},pos={w.pos},widget_id={id},{w}""" Logger.info(msg) diff --git a/kivyblocks/widgetExt/__init__.py b/kivyblocks/widgetExt/__init__.py index c5f70bf..9cf1d29 100755 --- a/kivyblocks/widgetExt/__init__.py +++ b/kivyblocks/widgetExt/__init__.py @@ -1,14 +1,13 @@ from kivy.utils import platform from .binstateimage import BinStateImage from .jsoncodeinput import JsonCodeInput -from .inputext import FloatInput,IntegerInput,StrInput,SelectInput, BoolInput, AmountInput, Password +from .inputext import FloatInput,IntegerInput,StrInput,SelectInput, BoolInput, Password from .scrollwidget import ScrollWidget from .messager import Messager __all__ = [ BinStateImage, JsonCodeInput, FloatInput, -AmountInput, Password, BoolInput, IntegerInput, diff --git a/kivyblocks/widgetExt/inputext.py b/kivyblocks/widgetExt/inputext.py index b84342f..284765b 100755 --- a/kivyblocks/widgetExt/inputext.py +++ b/kivyblocks/widgetExt/inputext.py @@ -10,10 +10,11 @@ from kivy.uix.dropdown import DropDown from kivy.uix.switch import Switch from kivy.metrics import sp,dp from kivy.app import App -from kivy.properties import BooleanProperty, ListProperty +from kivy.properties import BooleanProperty, ListProperty, \ + NumericProperty from ..threadcall import HttpClient -from ..utils import CSize +from ..utils import CSize, set_widget_height, set_widget_width from ..widget_css import WidgetCSS, get_css class BoolInput(Switch): @@ -34,12 +35,11 @@ class BoolInput(Switch): def setValue(self,v): self.active = v -class StrInput(TextInput): - def __init__(self,csscls='default', - **kv): +class StrInput(WidgetCSS, TextInput): + length = NumericProperty(None) + def __init__(self, **kv): if kv is None: kv = {} - css = get_css(csscls) a = { "allow_copy":True, "password":False, @@ -51,26 +51,17 @@ class StrInput(TextInput): "write_tab":False } a.update(kv) - a['background_color'] = css['bgcolor'] - a['foreground_color'] = css['fgcolor'] - w = kv.get('width',1) - h = kv.get('height',1) - if w <= 1: - a['size_hint_x'] = w - else: - a['size_hint_x'] = None - a['width'] = CSize(w) - if h <= 1: - a['size_hint_y'] = h - else: - a['size_hint_y'] = None - a['height'] = CSize(h) - super(StrInput, self).__init__(**a) self.text = self.old_value = '' self.register_event_type('on_changed') self.bind(on_text_validate=self.checkChange) + def on_bgcolor(self, *args): + self.background_color = self.bgcolor + + def on_fgcolor(self, *args): + self.foreground_color = self.fgcolor + def on_changed(self,v=None): pass @@ -80,6 +71,14 @@ class StrInput(TextInput): self.old_value = v self.dispatch('on_changed',v) + def insert_text(self, substring, from_undo=False): + if self.length: + if len(self.text) + len(substring) > self.length: + return None + ret = super().insert_text(substring, from_undo=from_undo) + # ret is None + return ret + def getValue(self): return self.text @@ -89,11 +88,10 @@ class StrInput(TextInput): self.text = str(v) self.old_value = self.text - class Password(StrInput): def __init__(self, **kw): - kw['password'] = True super().__init__(**kw) + self.password = True class IntegerInput(StrInput): def __init__(self,**kw): @@ -101,49 +99,23 @@ class IntegerInput(StrInput): a.update(kw) a['halign'] = 'right' super().__init__(**a) + self.input_filter = 'int' - pat = re.compile('[^0-9]') - def insert_text(self, substring, from_undo=False): - pat = self.pat - s = re.sub(pat, '', substring) - return StrInput.insert_text(self,s, from_undo=from_undo) - def on_focus(self,t,v): self.cursor = (0,len(self.text)) + def getValue(self): + return int(self.text) + class FloatInput(IntegerInput): - pat = re.compile('[^0-9]') - def filter(self,substring): - pat = self.pat - if '.' in self.text: - s = re.sub(pat, '', substring) - else: - s = '.'.join([re.sub(pat, '', s) for s in substring.split('.', 1)]) - return s + dec = NumericProperty(2) + def __init__(self,**kw): + super(FloatInput, self).__init__(**kw) + self.input_filter = 'float' - def insert_text(self, substring, from_undo=False): - s = self.filter(substring) - return StrInput.insert_text(self,s, from_undo=from_undo) + def getValue(self): + return float(self.text) -class AmountInput(FloatInput): - def filter(self,substring): - s = super(AmountInput,self).filter(substring) - a = s.split('.') - b = a[0] - if len(b)>3: - k = [] - while len(b)>3: - x = b[-3:] - k.insert(0,x) - b = b[:-3] - a[0] = ','.join(k) - s = '.'.join(a) - return '.'.join(a) - - def insert_text(self, substring, from_undo=False): - s = self.filter(substring) - return StrInput.insert_text(self,s, from_undo=from_undo) - class MyDropDown(DropDown): def __init__(self,csscls='input', **kw): super(MyDropDown,self).__init__()