kivyblocks/kivyblocks/form.py
2021-02-19 21:25:08 +08:00

402 lines
10 KiB
Python

from kivy.factory import Factory
from kivy.logger import Logger
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.graphics import Color
from kivycalendar import DatePicker
from .responsivelayout import VResponsiveLayout
from .widgetExt.inputext import FloatInput,IntegerInput, \
StrInput,SelectInput, BoolInput, AmountInput, Password
from .baseWidget import *
from .utils import *
from .i18n import I18n
from .toolbar import Toolbar
from .color_definitions import getColors
from .bgcolorbehavior import BGColorBehavior
"""
form options
{
"toolbar":
"dataloader":{
"dataurl":
"params":
"method"
}
"cols":"1"
"labelwidth":
"textsize":
"inputheight":
"inline":True,False
"fields":[
{
"name":
"label":
"datatype",
"uitype",
"uiparams",
"default",
"readonly",
"required"
},
]
"binds":[
]
}
"""
uitypes = {
"str":{
"orientation":"horizontal",
"wclass":StrInput,
},
"string":{
"orientation":"horizontal",
"wclass":StrInput,
},
"password":{
"orientation":"horizontal",
"wclass":Password,
},
"int":{
"orientation":"horizontal",
"wclass":IntegerInput,
},
"number":{
"orientation":"horizontal",
"wclass":IntegerInput,
},
"float":{
"orientation":"horizontal",
"wclass":FloatInput,
},
"amount":{
"orientation":"horizontal",
"wclass":AmountInput,
},
"date":{
"orientation":"horizontal",
"wclass":DatePicker,
},
"time":{
"orientation":"horizontal",
"wclass":StrInput,
},
"bool":{
"orientation":"horizontal",
"wclass":BoolInput,
},
"code":{
"orientation":"horizontal",
"wclass":SelectInput,
},
"text":{
"orientation":"vertical",
"wclass":StrInput,
"options":{
"multiline":True,
"height":9,
}
},
"teleno":{
"orientation":"horizontal",
"wclass":StrInput,
},
"email":{
"orientation":"horizontal",
"wclass":StrInput,
},
}
class InputBox(BoxLayout):
def __init__(self, form, **options):
self.form = form
self.options = options
self.uitype = options.get('uitype','string')
self.uidef = uitypes[self.uitype]
orientation = self.uidef['orientation']
width = self.form.inputwidth
height = self.form.inputheight
self.labelwidth = self.form.options['labelwidth']
kwargs = {
"orientation":orientation,
}
if height<=1:
kwargs['size_hint_y'] = height
else:
kwargs['size_hint_y'] = None
kwargs['height'] = CSize(height)
if width <= 1:
kwargs['size_hint_x'] = width
else:
kwargs['size_hint_x'] = None
kwargs['width'] = CSize(width)
super().__init__(**kwargs)
self.initflag = False
self.bind(size=self.setSize,
pos=self.setSize)
self.register_event_type("on_datainput")
self.register_event_type("on_ready")
def on_datainput(self,o,v=None):
print('on_datainput fired ...',o,v)
def init(self):
if self.initflag:
return
i18n = I18n()
opts = {
"orientation":"horizontal",
"size_hint_y":None,
"height":CSize(3)
}
if self.labelwidth<=1:
opts['size_hint_x'] = self.labelwidth
else:
opts['size_hint_x'] = None
opts['width'] = self.labelwidth
bl = BoxLayout(**opts)
Logger.info('kivyblock:labelwidth=%f,opts=%s', self.labelwidth,str(opts))
Logger.info('kivyblock:bl.widht=%f,bl.height=%f',bl.width,bl.height)
self.add_widget(bl)
label = self.options.get('label',self.options.get('name'))
kwargs = {
"i18n":True,
"text":label,
"font_size":CSize(1),
"size_hint_x":None,
"width":CSize(len(label)),
"size_hint_y":None,
"height":CSize(3)
}
self.labeltext = Text(**kwargs)
bl.add_widget(self.labeltext)
Logger.info('kivyblock:label.widht=%f,label.height=%f',
self.labeltext.width,self.labeltext.height)
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)
options = self.uidef.get('options',{}).copy()
options.update(self.options.get('uiparams',{}))
options['allow_copy'] = True
options['width'] = options.get('width',20)
options['height'] = options.get('height',1)
if self.options.get('tip'):
options['hint_text'] = i18n(self.options.get('tip'))
self.input_widget = self.uidef['wclass'](**options)
if self.options.get('readonly'):
self.input_widget.disabled = True
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'))
self.input_widget.widget_id = self.options['name']
self.add_widget(self.input_widget)
self.initflag = True
self.input_widget.bind(on_focus=self.on_focus)
self.dispatch('on_ready', self)
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
def on_ready(self, obj):
Logger.info('kivyblocks: Form input ready')
def clear(self):
self.input_widget.setValue('')
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 setSize(self,o,v=None):
self.init()
def setValue(self,v):
self.input_widget.setValue(v)
def getValue(self):
return {self.options.get('name'):self.input_widget.getValue()}
def disable(self,*args,**kwargs):
self.input_widget.disabled = True
def enable(self,*args,**kwargs):
self.input_widget.disabled = False
def defaultToolbar():
return {
"img_size":2,
"text_size":1,
"tools":[
{
"name":"__submit",
"img_src":"/imgs/submit.png",
"label":"submit"
},
{
"name":"__clear",
"img_src":"/imgs/clear.png",
"label":"clear"
}
]
}
class Form(BGColorBehavior, BoxLayout):
"""
Form format
{
"widgettype":"Form",
"options":{
"params":{parameters will using when using data_url and data_rfname}
"data_url":"get data from, if null, not data show at beginning",
"data_rfname":"get data from a register function",
"data_target":"a widget_id name to get data from",
"submit_url":"url where the submit data go to",
"submit_rfname":"register function to received the submited data"
"submit_target":"a widget to accepted the submited data"
"input_width":"width include label and input"
"label_width":"label width of the input"
"fields":[
{
"name":"field name,match the data record's field name",
"label":"data label",
"datatype":"data type, see data type list",
"uitype":"data show type, see uitype list"
}
]
def __init__(self, **options):
self.options = options
BoxLayout.__init__(self, orientation='vertical')
self.color_level = self.options.get('color_level', 0)
BGColorBehavior.__init__(self,
color_level=self.options.get('color_level',-1),
radius=self.options.get('radius',[]))
self.readiedInput = 0
self.cols = self.options_cols = self.options.get('cols',1)
if isHandHold() and Window.width < Window.height:
self.cols = 1
self.inputwidth = Window.width / self.cols
self.inputheight = self.options.get('inputheight',3)
self.init()
self.register_event_type('on_submit')
def on_size(self, *args):
pass
def init(self):
self.toolbar = Toolbar(**self.options.get('toolbar',defaultToolbar()))
self.fsc = VResponsiveLayout(
self.inputwidth,
self.cols
)
self.add_widget(self.toolbar)
self.add_widget(self.fsc)
self.fieldWidgets=[]
for f in self.options['fields']:
w = InputBox(self, **f)
self.fsc.add_widget(w)
self.fieldWidgets.append(w)
w.bind(on_ready=self.makeInputLink)
wid = Factory.Blocks.getWidgetById('__submit',from_widget=self)
wid.bind(on_press=self.on_submit_button)
wid = Factory.Blocks.getWidgetById('__clear',from_widget=self)
wid.bind(on_press=self.on_clear_button)
def makeInputLink(self,o,v=None):
self.readiedInput += 1
if self.readiedInput >= len(self.options['fields']):
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
def getValue(self):
d = {}
for f in self.fieldWidgets:
v = f.getValue()
d.update(v)
return d
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
def on_submit(self,v=None):
print('Form():on_submit fired ...',v)
return False
def on_submit_button(self,o,v=None):
Logger.info('kivyblcks: submit button press')
if not self.checkData():
Logger.info('kivyblocks: CheckData False')
return
d = self.getValue()
Logger.info('kivyblocks: fire on_submit')
self.dispatch('on_submit',d)
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))
self.input_widget = StrInput(
text='',
multiline=False,
allow_copy=True,
halign='middle',
font_size=1,
size_hint_y=None,
size_hint_x=1,
height=2)
self.add_widget(self.input_widget)
self.register_event_type('on_submit')
v = options.get('value',options.get('default_value',''))
self.input_widget.setValue(v)
self.input_widget.bind(on_text_validate=self.submit_input)
def getValue(self):
d = {
self.name:self.input_widget.text
}
return d
def submit_input(self,o,v=None):
text = self.input_widget.text
if text != '':
d = {
self.name:text
}
self.dispatch('on_submit',d)
def on_submit(self,v=None):
print('StrSearchForm():on_submit fired ..........')