kivyblocks/kivyblocks/form.py

441 lines
12 KiB
Python
Raw Normal View History

2022-01-27 11:07:46 +08:00
from math import floor
2020-11-20 19:43:05 +08:00
from kivy.factory import Factory
2022-01-07 14:40:19 +08:00
from kivy.properties import NumericProperty, StringProperty, \
DictProperty, ListProperty, OptionProperty, BooleanProperty
2020-04-02 13:18:10 +08:00
from kivy.logger import Logger
2019-12-19 11:13:47 +08:00
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
2022-01-27 11:07:46 +08:00
from kivy.uix.stacklayout import StackLayout
2019-12-19 11:13:47 +08:00
from kivy.core.window import Window
2020-03-25 16:28:29 +08:00
from kivy.graphics import Color
2020-01-08 18:04:39 +08:00
from .responsivelayout import VResponsiveLayout
2019-12-19 11:13:47 +08:00
from .baseWidget import *
from .utils import *
2020-12-10 23:47:30 +08:00
from .i18n import I18n
2020-01-08 18:04:39 +08:00
from .toolbar import Toolbar
2020-04-02 13:18:10 +08:00
from .color_definitions import getColors
2021-02-21 14:27:01 +08:00
from .dataloader import DataGraber
from .ready import WidgetReady
2021-05-24 12:25:29 +08:00
from .widget_css import WidgetCSS
2021-07-24 18:19:52 +08:00
from .dateinput import DateInput
2022-01-27 11:07:46 +08:00
from .uitype.factory import UiFactory # import get_input_builder
2020-04-02 13:18:10 +08:00
2019-12-19 11:13:47 +08:00
"""
form options
{
"toolbar":
"dataloader":{
2021-02-20 09:39:20 +08:00
"dataurl":"first"
2022-01-07 14:40:19 +08:00
"datatargment":"second",
2021-02-20 09:39:20 +08:00
"datarfname":"third"
2019-12-19 11:13:47 +08:00
"params":
"method"
}
"cols":"1"
"labelwidth":
"textsize":
"inputheight":
"inline":True,False
"fields":[
{
"name":
"label":
"datatype",
"uitype",
"uiparams",
"default",
2019-12-21 18:28:00 +08:00
"readonly",
2019-12-19 11:13:47 +08:00
"required"
},
]
"binds":[
]
}
"""
class InputBox(BoxLayout):
def __init__(self, form, **options):
2022-01-07 14:40:19 +08:00
self.input_widget = None
2019-12-19 11:13:47 +08:00
self.form = form
self.options = options
2022-01-01 06:14:49 +08:00
self.uitype = options.get('uitype',options.get('datatype','str'))
2022-01-07 14:40:19 +08:00
self.size_hint = (None, None)
2019-12-19 11:13:47 +08:00
height = self.form.inputheight
2021-02-21 14:27:01 +08:00
if self.uitype == 'text':
if not options.get('height'):
self.options['height'] = 4
height = self.options.get('height',4)
2021-02-26 15:50:20 +08:00
self.labelwidth = self.form.labelwidth
2019-12-19 11:13:47 +08:00
kwargs = {
2022-01-01 06:14:49 +08:00
"orientation":'horizontal',
2019-12-19 11:13:47 +08:00
}
2020-04-24 18:35:30 +08:00
if height<=1:
kwargs['size_hint_y'] = height
else:
kwargs['size_hint_y'] = None
kwargs['height'] = CSize(height)
2022-01-27 11:07:46 +08:00
if self.form.input_width <= 1:
kwargs['size_hint_x'] = self.form.input_width
2019-12-19 11:13:47 +08:00
else:
kwargs['size_hint_x'] = None
2022-01-27 11:07:46 +08:00
kwargs['width'] = self.form.input_width
2019-12-19 11:13:47 +08:00
super().__init__(**kwargs)
self.initflag = False
self.register_event_type("on_datainput")
2022-01-27 11:07:46 +08:00
self.init()
def focus_act(self, *args):
2022-01-29 16:32:51 +08:00
pass
2022-01-27 11:07:46 +08:00
2019-12-19 11:13:47 +08:00
2022-01-07 14:40:19 +08:00
def on_size(self, *args):
if self.input_widget is None:
return
self.labeltext.size_hint_x = None
if self.form.labelwidth < 1:
self.labeltext.width = self.width * self.form.labelwidth - \
CSize(1)
else:
self.labeltext.width = CSize(self.form.labelwidth)
self.input_widget.size_hint_x = None
self.input_widget.width = self.width - self.labeltext.width - CSize(1)
2022-01-27 11:07:46 +08:00
2020-01-08 18:04:39 +08:00
def on_datainput(self,o,v=None):
2022-01-29 16:32:51 +08:00
pass
#print('on_datainput fired ...',o,v)
2020-01-08 18:04:39 +08:00
2019-12-19 11:13:47 +08:00
def init(self):
if self.initflag:
return
2020-04-02 13:18:10 +08:00
i18n = I18n()
2019-12-19 11:13:47 +08:00
label = self.options.get('label',self.options.get('name'))
kwargs = {
2020-12-10 23:47:30 +08:00
"i18n":True,
2022-01-01 06:14:49 +08:00
"otext":label,
2020-01-08 18:04:39 +08:00
"font_size":CSize(1),
2019-12-19 11:13:47 +08:00
}
2022-01-27 11:07:46 +08:00
if self.form.labelwidth <= 1:
2022-01-07 14:40:19 +08:00
kwargs['size_hint_x'] = self.form.labelwidth
else:
kwargs['size_hint_x'] = None
kwargs['width'] = CSize(self.form.labelwidth)
2020-12-10 23:47:30 +08:00
self.labeltext = Text(**kwargs)
2022-01-27 11:07:46 +08:00
self.add_widget(self.labeltext)
2020-03-25 16:28:29 +08:00
if self.options.get('required',False):
star = Label(text='*',
color=(1,0,0,1),
size_hint_x=None,
width=CSize(1))
2022-01-27 11:07:46 +08:00
self.add_widget(star)
2022-01-01 06:14:49 +08:00
options = self.options.copy()
options['hint_text'] = i18n(self.options.get('hint_text'))
2022-01-27 11:07:46 +08:00
options['size_hint_y'] = None
options['height'] = self.height
self.input_widget = UiFactory.build_input_widget(options)
try:
self.input_widget.bind(focus=self.focus_act)
except:
pass
self.input_widget.height = self.height
self.input_widget.size_hint_y = None
2022-01-07 14:40:19 +08:00
if self.form.labelwidth < 1:
self.input_widget.size_hint_x = 1 - self.form.labelwidth
else:
self.input_widget.size_hint_x = None
self.input_widget.width = self.width - self.labeltext.width - CSize(1)
2019-12-21 18:28:00 +08:00
if self.options.get('readonly'):
self.input_widget.disabled = True
2021-02-03 18:12:12 +08:00
if self.options.get('value'):
self.input_widget.setValue(self.options.get('value'))
elif self.options.get('default_value'):
self.input_widget.setValue(self.options.get('default_value'))
2020-11-19 12:22:16 +08:00
self.input_widget.widget_id = self.options['name']
2019-12-19 11:13:47 +08:00
self.add_widget(self.input_widget)
2022-01-01 06:14:49 +08:00
if self.labelwidth<=1:
self.labeltext.size_hint_x = self.labelwidth
self.input_widget.size_hint_x = 1 - self.labelwidth
else:
self.labeltext.size_hint_x = None
self.labeltext.width = CSize(self.labelwidth)
2019-12-19 11:13:47 +08:00
self.initflag = True
self.input_widget.bind(on_focus=self.on_focus)
2021-02-21 14:27:01 +08:00
2020-04-02 13:18:10 +08:00
def check(self):
d = self.getValue()
v = d.get(self.options.get('name'))
Logger.info('InputWidget() getValue=%s, name=%s',
v,self.options.get('name'))
if self.options.get('required',False) and \
(v == '' or v is None):
return False
return True
2019-12-21 18:28:00 +08:00
def clear(self):
self.input_widget.setValue('')
2019-12-19 11:13:47 +08:00
def on_focus(self,o,v):
if v:
self.old_value = o.text
else:
if self.old_value != o.text:
self.dispatch('on_datainput',o.text)
def setValue(self,v):
self.input_widget.setValue(v)
def getValue(self):
2020-01-08 18:04:39 +08:00
return {self.options.get('name'):self.input_widget.getValue()}
2019-12-19 11:13:47 +08:00
2020-04-24 18:35:30 +08:00
def disable(self,*args,**kwargs):
self.input_widget.disabled = True
def enable(self,*args,**kwargs):
self.input_widget.disabled = False
2019-12-19 11:13:47 +08:00
def defaultToolbar():
return {
2022-01-07 14:40:19 +08:00
"img_size_c":1,
"text_size_c":1,
"toolbar_orient":"H",
"tool_orient":"horizontal",
2020-01-08 18:04:39 +08:00
"tools":[
2019-12-19 11:13:47 +08:00
{
"name":"__submit",
2022-01-07 14:40:19 +08:00
"source_on":blockImage("save.png"),
"source_off":blockImage("save.png"),
2019-12-19 11:13:47 +08:00
"label":"submit"
},
{
"name":"__clear",
2022-01-07 14:40:19 +08:00
"source_on":blockImage("clear.png"),
"source_off":blockImage("clear.png"),
2019-12-19 11:13:47 +08:00
"label":"clear"
}
]
}
2021-05-24 12:25:29 +08:00
class Form(WidgetCSS, WidgetReady, BoxLayout):
2022-01-27 11:07:46 +08:00
cols = NumericProperty(None)
2022-01-07 14:40:19 +08:00
csscls=StringProperty('formcss')
input_css=StringProperty('formfieldcss')
inputwidth=NumericProperty(1)
inputheight=NumericProperty(3)
labelwidth=NumericProperty(0.3)
toolbar_at=OptionProperty('top', \
options=['top','bottom','left','right'])
dataloader=DictProperty(None)
fields = ListProperty([])
toolbar=DictProperty(None)
submit=DictProperty(None)
clear=DictProperty(None)
notoolbar=BooleanProperty(False)
2022-01-27 11:07:46 +08:00
def __init__(self, **options):
self.cols = 1
2022-01-29 16:32:51 +08:00
self.toolbar_w = None
2021-02-26 15:50:20 +08:00
if self.toolbar_at in ['top','bottom']:
options['orientation'] = 'vertical'
else:
options['orientation'] = 'horizontal'
2022-01-07 14:40:19 +08:00
SUPER(Form, self, options)
2022-01-27 11:07:46 +08:00
self.setup_cols_and_inputwidth()
2020-04-24 18:35:30 +08:00
self.init()
2020-02-26 17:24:58 +08:00
self.register_event_type('on_submit')
2019-12-19 11:13:47 +08:00
2022-01-27 11:07:46 +08:00
def setup_cols_and_inputwidth(self):
if isHandHold() and Window.width < Window.height:
self._cols = 1
self.input_width = 1
return
if self.cols is None and self.inputwidth is None:
self.input_width = 1
self._cols = 1
return
if self.inputwidth > 1:
self.input_width = CSize(self.inputwidth)
self._cols = -1 # auto calculate
return
2022-01-30 00:08:49 +08:00
self.input_width = self.inputwidth
2022-01-27 11:07:46 +08:00
self._cols = 1
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
2021-02-19 21:25:08 +08:00
def on_size(self, *args):
2021-02-26 15:50:20 +08:00
if not hasattr(self,'fsc'):
return
if not self.notoolbar:
if self.toolbar_at in ['top', 'bottom']:
2022-01-07 14:40:19 +08:00
self.fsc.height = self.height - self.toolbar_w.height
2021-02-26 15:50:20 +08:00
else:
2022-01-07 14:40:19 +08:00
self.fsc.width = self.width - self.toolbar_w.width
2021-02-26 15:50:20 +08:00
else:
if self.toolbar_at in ['top', 'bottom']:
self.fsc.height = self.height
else:
self.fsc.width = self.width
2022-01-27 11:07:46 +08:00
self.set_grid_attrs()
2021-02-19 21:25:08 +08:00
2019-12-19 11:13:47 +08:00
def init(self):
2021-02-26 15:50:20 +08:00
if not self.notoolbar:
desc = defaultToolbar()
desc1 = self.toolbar
2022-01-07 14:40:19 +08:00
tools = desc['tools'].copy()
2021-02-26 15:50:20 +08:00
if desc1:
2022-01-07 14:40:19 +08:00
desc.update(desc1)
tools = tools + desc1['tools']
2021-02-26 15:50:20 +08:00
if self.submit:
kw = self.submit.copy()
if kw.get('name'):
del kw['name']
2022-01-07 14:40:19 +08:00
for t in tools:
2021-02-26 15:50:20 +08:00
if t['name'] == '__submit':
t.update(kw)
if self.clear:
kw = self.clear.copy()
if kw.get('name'):
del kw['name']
2022-01-07 14:40:19 +08:00
for t in tools:
2021-02-26 15:50:20 +08:00
if t['name'] == '__clear':
t.update(kw)
2022-01-07 14:40:19 +08:00
desc['tools'] = tools
2021-02-26 15:50:20 +08:00
if self.toolbar_at in ['top', 'bottom']:
2022-01-07 14:40:19 +08:00
desc['toolbar_orient'] = 'H'
2021-02-26 15:50:20 +08:00
else:
2022-01-07 14:40:19 +08:00
desc['toolbar_orient'] = 'V'
desc['tool_orient'] = 'veritcal'
2022-01-29 16:32:51 +08:00
self.toolbar_w = Factory.Toolbar(**desc)
2020-01-09 17:52:02 +08:00
self.fsc = VResponsiveLayout(
2022-01-27 11:07:46 +08:00
box_width = self.input_width,
2021-02-26 15:50:20 +08:00
size_hint=(1,1)
2020-01-09 17:52:02 +08:00
)
2021-02-26 15:50:20 +08:00
2022-01-29 16:32:51 +08:00
if self.toolbar_at in ['top', 'left'] and self.toolbar_w:
2022-01-07 14:40:19 +08:00
self.add_widget(self.toolbar_w)
2020-01-08 18:04:39 +08:00
self.add_widget(self.fsc)
2022-01-29 16:32:51 +08:00
if self.toolbar_at in ['bottom', 'right'] and self.toolbar_w:
2022-01-07 14:40:19 +08:00
self.add_widget(self.toolbar_w)
2021-02-26 15:50:20 +08:00
2019-12-19 11:13:47 +08:00
self.fieldWidgets=[]
2021-02-26 15:50:20 +08:00
for f in self.fields:
2020-01-08 18:04:39 +08:00
w = InputBox(self, **f)
2019-12-19 11:13:47 +08:00
self.fsc.add_widget(w)
self.fieldWidgets.append(w)
2020-04-02 13:18:10 +08:00
w.bind(on_ready=self.makeInputLink)
2020-11-20 19:43:05 +08:00
wid = Factory.Blocks.getWidgetById('__submit',from_widget=self)
2021-02-26 15:50:20 +08:00
if wid:
wid.bind(on_press=self.on_submit_button)
2020-11-20 19:43:05 +08:00
wid = Factory.Blocks.getWidgetById('__clear',from_widget=self)
2021-02-26 15:50:20 +08:00
if wid:
wid.bind(on_press=self.on_clear_button)
if self.dataloader:
self.loader = DataGraber(**self.dataloader)
d = self.loader.load()
2021-02-21 14:27:01 +08:00
if d:
self.setValue(d)
2021-02-26 15:50:20 +08:00
self.on_size()
2021-02-21 14:27:01 +08:00
2020-04-02 13:18:10 +08:00
def makeInputLink(self,o,v=None):
self.readiedInput += 1
2021-02-26 15:50:20 +08:00
if self.readiedInput >= len(self.fields):
2020-04-02 13:18:10 +08:00
p = self.fieldWidgets[0]
for w in self.fieldWidgets[1:]:
p.input_widget.focus_next = w.input_widget
w.input_widget.focus_previous = p.input_widget
p = w
2021-02-21 14:27:01 +08:00
def setValue(self,d):
for f in self.fieldWidgets:
v = f.getValue()
for k in v.keys():
f.setValue({k:d[k]})
2020-12-10 23:47:30 +08:00
def getValue(self):
2020-01-08 18:04:39 +08:00
d = {}
for f in self.fieldWidgets:
v = f.getValue()
d.update(v)
return d
2020-04-02 13:18:10 +08:00
def checkData(self):
for w in self.fieldWidgets:
if not w.check():
w.input_widget.focus = True
Logger.info('kivyblcks: input check false')
return False
Logger.info('kivyblcks: input check success')
return True
2022-01-07 14:40:19 +08:00
def on_submit(self,o, v=None):
2020-02-26 17:24:58 +08:00
return False
2020-01-08 18:04:39 +08:00
2019-12-19 11:13:47 +08:00
def on_submit_button(self,o,v=None):
2020-04-02 13:18:10 +08:00
Logger.info('kivyblcks: submit button press')
if not self.checkData():
Logger.info('kivyblocks: CheckData False')
return
2020-12-10 23:47:30 +08:00
d = self.getValue()
2020-04-02 13:18:10 +08:00
Logger.info('kivyblocks: fire on_submit')
2020-02-26 17:24:58 +08:00
self.dispatch('on_submit',d)
2019-12-19 11:13:47 +08:00
def on_clear_button(self,o,v=None):
for fw in self.fieldWidgets:
fw.clear()
class StrSearchForm(BoxLayout):
def __init__(self,img_url=None,**options):
self.name = options.get('name','search_string')
BoxLayout.__init__(self,orientation='horizontal',size_hint_y=None,height=CSize(3))
2021-05-24 12:25:29 +08:00
self.input_css = options.get('input_css', 'input')
i18n = I18n()
2021-02-03 18:12:12 +08:00
self.input_widget = StrInput(
2019-12-22 22:02:37 +08:00
text='',
2019-12-19 11:13:47 +08:00
multiline=False,
2021-05-24 12:25:29 +08:00
csscls=self.input_css,
hint_text=i18n(options.get('tip',options.get('hint_text',''))),
2020-03-25 09:51:22 +08:00
allow_copy=True,
2021-05-24 12:25:29 +08:00
font_size=CSize(1),
2019-12-19 11:13:47 +08:00
size_hint_y=None,
2021-02-03 18:12:12 +08:00
size_hint_x=1,
2021-05-24 12:25:29 +08:00
height=3)
2020-04-02 13:18:10 +08:00
self.add_widget(self.input_widget)
2019-12-19 11:13:47 +08:00
self.register_event_type('on_submit')
2021-02-03 18:12:12 +08:00
v = options.get('value',options.get('default_value',''))
self.input_widget.setValue(v)
2020-04-02 13:18:10 +08:00
self.input_widget.bind(on_text_validate=self.submit_input)
2019-12-19 11:13:47 +08:00
2020-12-10 23:47:30 +08:00
def getValue(self):
2020-03-19 18:00:35 +08:00
d = {
2020-04-02 13:18:10 +08:00
self.name:self.input_widget.text
2020-03-19 18:00:35 +08:00
}
return d
2021-02-21 14:27:01 +08:00
def setValue(self, d):
if isinstance(d,str):
self.input_widget.text = d
if isinstance(d,{}):
self.input_widget.text = d.get(self.name,'')
2019-12-19 11:13:47 +08:00
def submit_input(self,o,v=None):
2020-04-02 13:18:10 +08:00
text = self.input_widget.text
2019-12-19 11:13:47 +08:00
if text != '':
d = {
self.name:text
}
self.dispatch('on_submit',d)
2020-02-26 17:24:58 +08:00
def on_submit(self,v=None):
2022-01-29 16:32:51 +08:00
pass