This commit is contained in:
yumoqing 2020-04-24 18:35:30 +08:00
parent 97372e8ad2
commit e11a398439
17 changed files with 645 additions and 297 deletions

View File

@ -1,4 +1,6 @@
import sys
from traceback import print_exc
from kivy.app import App
from kivy.utils import platform
from kivy.uix.button import Button, ButtonBehavior
from kivy.uix.accordion import Accordion,AccordionItem
@ -46,6 +48,7 @@ from kivy.uix.codeinput import CodeInput
from kivy.graphics import Color, Rectangle
from kivy.properties import ListProperty
from kivycalendar import DatePicker
from kivy.factory import Factory
from appPublic.dictObject import DictObject
@ -57,52 +60,66 @@ from .widgetExt.inputext import FloatInput,IntegerInput, \
from .widgetExt.messager import Messager
from .charts.bar import Bar
from .bgcolorbehavior import BGColorBehavior
from .utils import NeedLogin, InsufficientPrivilege, HTTPError
from .login import LoginForm
if platform == 'android':
from .widgetExt.phonebutton import PhoneButton
from .widgetExt.androidwebview import AWebView
class Text(BGColorBehavior, Label):
def __init__(self,bgcolor=[],fgcolor=[],color_level=-1,**kw):
self.options = DictObject(**kw)
kwargs = kw.copy()
Label.__init__(self,**kwargs)
BGColorBehavior.__init__(self,bgcolor=bgcolor,
fgcolor=fgcolor,
color_level=color_level)
class PressableImage(ButtonBehavior,AsyncImage):
def on_press(self):
pass
class Text(BGColorBehavior, Label):
def __init__(self,bgcolor=[0,0,0,1],**kw):
self.options = DictObject(**kw)
kwargs = kw.copy()
if kwargs.get('bgColor'):
self.bgColor = kwargs['bgColor']
del kwargs['bgColor']
Label.__init__(self,**kwargs)
BGColorBehavior.__init__(self,bgcolor=bgcolor)
class PressableLabel(ButtonBehavior, Text):
def on_press(self):
pass
class HTTPDataHandler(EventDispatcher):
def __init__(self, url,method='GET',params={},headers={}):
EventDispatcher.__init__()
self.opts = opts
def __init__(self, url,method='GET',params={},
headers={},
files={}
):
EventDispatcher.__init__(self)
self.url = url
self.method = method
self.params = params
self.headers = headers
self.files=files
self.hc = App.get_running_app().hc
self.register_event_type('on_sucess')
self.register_event_type('on_success')
self.register_event_type('on_error')
def on_success(self,o,v):
def on_success(self,v):
pass
def on_error(self,o,e):
def on_error(self,e):
pass
def onSuccess(self,o,v):
self.dispatch('on_sucess',v)
print(__file__,'onSuccess():v=',v)
self.dispatch('on_success',v)
def onError(self,o,e):
if isinstance(e,NeedLogin):
lf = LoginForm()
lf.bind(on_setupuser=self.redo)
lf.open()
return
self.dispatch('on_error',e)
print_exc()
print('[****][*********] onError Called',e)
def redo(self,o,v=None):
self.handle()
def handle(self,params={},headers={}):
p = self.params

View File

@ -1,27 +1,87 @@
from kivy.logger import Logger
from kivy.graphics import Color, Rectangle
from kivy.properties import ListProperty
from .color_definitions import getColors
_logcnt = 0
class BGColorBehavior(object):
bgcolor = ListProperty([])
def __init__(self, bgcolor=[0,0,0,1],**kwargs):
def __init__(self, bgcolor=[],fgcolor=[], color_level=-1,**kwargs):
self.color_level = color_level
self.bgcolor = bgcolor
self.fgcolor = fgcolor
self.normal_bgcolor = bgcolor
self.normal_fgcolor = fgcolor
self.selected_bgcolor = bgcolor
self.selected_fgcolor = fgcolor
self.useOwnColor = False
self.useOwnBG = False
self.useOwnFG = False
if color_level != -1:
fg,bg= getColors(color_level)
self.fgcolor = fg
self.bgcolor = bg
self.normal_bgcolor = bg
self.normal_fgcolor = fg
fg,bg= getColors(color_level,selected=True)
self.selected_bgcolor = bg
self.selected_fgcolor = fg
self.useOwnColor = True
else:
if bgcolor != [] and fgcolor != []:
self.useOwnColor = True
elif bgcolor != []:
self.useOwnBG = True
elif fgcolor != []:
self.useOwnFG = True
if self.fgcolor!=[]:
self.color = self.fgcolor
self.bind(size=self.onSize_bgcolor_behavior,
pos=self.onSize_bgcolor_behavior)
self.bind(parent=self.useAcestorColor)
self.on_bgcolor()
def useAcestorColor(self,o,v=None):
if self.useOwnColor:
return
p = self.parent
cnt = 0
while p:
if isinstance(p,BGColorBehavior):
break
p = p.parent
cnt += 1
if cnt > 100:
return
if not self.useOwnBG and p:
self.bgcolor = p.bgcolor
self.on_bgcolor()
if not self.useOwnFG and p:
self.fgcolor = p.bgcolor
self.color = self.fgcolor
def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'):
self.on_bgcolor()
else:
self.rect.pos = o.pos
self.rect.size = o.size
self.rect.pos = self.pos
self.rect.size = self.size
def on_bgcolor(self,o=None,v=None):
if self.bgcolor == []:
return
if self.canvas:
with self.canvas.before:
Color(*self.bgcolor)
self.rect = Rectangle(pos=self.pos,
size=self.size)
def selected(self):
self.bgcolor = self.selected_bgcolor
Logger.info('selected:color=%s',self.bgcolor)
self.on_bgcolor()
def unselected(self):
self.bgcolor = self.normal_bgcolor
Logger.info('unselected:color=%s',self.bgcolor)
self.on_bgcolor()

View File

@ -10,6 +10,7 @@ from appPublic.dictExt import dictExtend
from appPublic.folderUtils import ProgramPath
from appPublic.dictObject import DictObject
from appPublic.Singleton import SingletonDecorator
from appPublic.datamapping import keyMapping
from kivy.config import Config
from kivy.metrics import sp,dp,mm
@ -17,14 +18,11 @@ from kivy.core.window import WindowBase
from kivy.properties import BooleanProperty
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.factory import Factory
from .baseWidget import *
from .widgetExt import Messager
from .externalwidgetmanager import ExternalWidgetManager
from .threadcall import HttpClient
from .toolbar import *
from .dg import DataGrid
from .utils import *
from .ready import WidgetReady
from .serverImageViewer import ServerImageViewer
from .vplayer import VPlayer
from .form import InputBox, Form, StrSearchForm
@ -113,10 +111,12 @@ def registerWidget(name,widget):
globals()[name] = widget
class Blocks:
registedWidgets = {}
class Blocks(EventDispatcher):
def __init__(self):
EventDispatcher.__init__(self)
self.action_id = 0
self.register_event_type('on_built')
self.register_event_type('on_failed')
def register_widget(self,name,widget):
globals()[name] = widget
@ -140,8 +140,6 @@ class Blocks:
raise Exception('None Function')
func =partial(f,widget)
return func
setattr(widget,fname,f)
return getattr(widget,fname)
def eval(self,s,l):
g = {}
@ -163,23 +161,22 @@ class Blocks:
return eval(s,g,l)
def getUrlData(self,kw,parenturl=None):
url = kw.get('url')
method = kw.get('method','GET')
params = kw.get('params',{})
if url is None:
print('kw=',kw)
raise ArgumentError('url','getUrlData() miss a url argument')
url = absurl(url,parenturl)
def getUrlData(self,url,method='GET',params={}, files={},
callback=None,
errback=None,**kw):
if url.startswith('file://'):
filename = url[7:]
print(filename)
with codecs.open(filename,'r','utf-8') as f:
b = f.read()
dic = json.loads(b)
callback(None,dic)
else:
dic = App.get_running_app().hc(url,method=method,params=params)
return dic, url
h = HTTPDataHandler(url,method=method,params=params,
files=files)
h.bind(on_success=callback)
h.bind(on_error=errback)
h.handle()
def strValueExpr(self,s:str,localnamespace:dict={}):
if not s.startswith('py::'):
@ -239,7 +236,6 @@ class Blocks:
def __build(self,desc:dict,ancestor=None):
def checkReady(w,o):
print('checkReady():w=',w,'o=',o)
w.children_ready[o] = True
if all(w.children_ready.values()):
w.ready = True
@ -250,8 +246,10 @@ class Blocks:
widgetClass = desc['widgettype']
opts = desc.get('options',{})
klass = globals().get(widgetClass)
if not klass:
try:
klass = Factory.get(widgetClass)
except Exception as e:
print('Error:',widgetClass,'not registered')
raise NotExistsObject(widgetClass)
widget = klass(**opts)
if desc.get('parenturl'):
@ -270,23 +268,34 @@ class Blocks:
widget.build_desc = desc
self.build_rest(widget,desc,ancestor)
# f = partial(self.build_rest,widget,desc,ancestor)
# event = Clock.schedule_once(f,0)
return widget
def build_rest(self, widget,desc,ancestor,t=None):
for sw in desc.get('subwidgets',[]):
w = self.widgetBuild(sw, ancestor=ancestor)
if w is None:
continue
bcnt = 0
btotal = len(desc.get('subwidgets',[]))
def doit(self,widget,bcnt,btotal,desc,o,w):
bcnt += 1
w.parenturl = widget.parenturl
widget.add_widget(w)
if hasattr(widget,'addChild'):
widget.addChild(w)
if bcnt >= btotal:
for b in desc.get('binds',[]):
self.buildBind(widget,b)
def doerr(o,e):
raise e
f = partial(doit,self,widget,bcnt,btotal,desc)
for sw in desc.get('subwidgets',[]):
b = Blocks()
b.bind(on_built=f)
b.bind(on_failed=doerr)
w = b.widgetBuild(sw, ancestor=ancestor)
if btotal == 0:
for b in desc.get('binds',[]):
self.buildBind(widget,b)
if hasattr(widget, 'built'):
widget.built()
def buildBind(self,widget,desc):
wid = desc.get('wid','self')
@ -309,7 +318,7 @@ class Blocks:
return self.scriptAction(widget, desc)
if acttype == 'method':
return self.methodAction(widget, desc)
alert(msg="actiontype invalid",title=acttype)
alert("actiontype(%s) invalid" % acttype,title='error')
def blocksAction(widget,desc):
target = self.getWidgetByIdPath(widget, desc.get('target','self'))
@ -319,13 +328,18 @@ class Blocks:
p = opts.get('options',{})
p.update(d)
opts['options'] = p
x = self.widgetBuild(opts,ancestor=widget)
if x is None:
alert(str(opts), title='widugetBuild error')
return
def doit(target,add_mode,o,w):
if add_mode == 'replace':
target.clear_widgets()
target.add_widget(x)
target.add_widget(w)
def doerr(o,e):
raise e
b = Blocks()
b.bind(on_built=partial(doit,target,add_mode))
b.bind(on_failed=doerr)
b.widgetBuild(opts,ancestor=widget)
def urlwidgetAction(self,widget,desc):
target = self.getWidgetByIdPath(widget, desc.get('target','self'))
@ -339,12 +353,19 @@ class Blocks:
'widgettype' : 'urlwidget'
}
d['options'] = opts
x = self.widgetBuild(d,ancestor=widget)
if x is None:
return
def doit(target,add_mode,o,w):
if add_mode == 'replace':
target.clear_widgets()
target.add_widget(x)
target.add_widget(w)
def doerr(o,e):
raise e
b = Blocks()
b.bind(on_built=partial(doit,target,add_mode))
b.bind(on_failed=doerr)
b.widgetBuild(d,ancestor=widget)
def getActionData(self,widget,desc):
data = {}
@ -352,6 +373,8 @@ class Blocks:
dwidget = self.getWidgetByIdPath(widget,
desc.get('datawidget'))
data = dwidget.getData()
if desc.get('keymapping'):
data = keyMapping(data, desc.get('keymapping'))
return data
def registedfunctionAction(self, widget, desc):
@ -361,7 +384,7 @@ class Blocks:
params = desc.get(params,{})
d = self.getActionData(widget,desc)
params.update(d)
func(params)
func(**params)
def scriptAction(self, widget, desc):
script = desc.get('script')
@ -400,28 +423,44 @@ class Blocks:
]
}
"""
def doit(desc):
desc = self.valueExpr(desc)
try:
widget = self.__build(desc,ancestor=ancestor)
self.dispatch('on_built',widget)
print('widgetBuild():&&&&&&&&&&&&&&&&&',widget)
except Exception as e:
print('&&&&&&&&&&&&&&&&&',e)
doerr(None,e)
print('&&&&&&&&&&&&&&&&&',e)
return
def doerr(o,e):
self.dispatch('on_failed',e)
name = desc['widgettype']
if name == 'urlwidget':
opts = desc.get('options')
parenturl = None
url=''
if ancestor:
parenturl = ancestor.parenturl
desc, parenturl = self.getUrlData(opts,parenturl)
if not isinstance(desc,dict):
print("getUrlData() return error data",opts,parenturl)
# alert(str(desc) + 'format error',title='widgetBuild()')
return None
desc = self.valueExpr(desc)
desc['parenturl'] = parenturl
try:
widget = self.__build(desc,ancestor=ancestor)
return widget
except:
print('widgetBuild() Error',desc)
print_exc()
alert(str(desc))
return None
url = absurl(opts.get('url'),parenturl)
except Exception as e:
self.dispatch('on_failed',e)
def cb(o,d):
try:
d['parenturl'] = url
doit(d)
except Exception as e:
doerr(None,e)
del opts['url']
self.getUrlData(url,callback=cb,errback=doerr,**opts)
return
doit(desc)
def getWidgetByIdPath(self,widget,path):
ids = path.split('/')
@ -438,3 +477,10 @@ class Blocks:
raise WidgetNotFoundById(id)
return widget
def on_built(self,v=None):
return
def on_failed(self,e=None):
return
Factory.register('Blocks',Blocks)

View File

@ -15,7 +15,10 @@ BoxViewer options:
}
}
"""
from traceback import print_exc
from functools import partial
from kivy.app import App
from kivy.factory import Factory
from kivy.utils import platform
from kivy.uix.boxlayout import BoxLayout
from .responsivelayout import VResponsiveLayout
@ -38,16 +41,11 @@ class BoxViewer(BoxLayout):
if options.get('toolbar'):
self.toolbar = Toolbar(options['toolbar'])
lopts = options['dataloader'].copy()
lopts['options']['adder'] = self.showObject
lopts['options']['remover'] = self.viewContainer.remove_widget
lopts['options']['clearer'] = self.viewContainer.clear_widgets
lopts['options']['target'] = self
lopts['options']['locater'] = self.locater
LClass = RelatedLoader
if lopts['widgettype'] == 'paging':
LClass = Paging
self.dataloader = RelatedLoader(target=self,**lopts['options'])
self.dataloader.bind(on_deletepage=self.deleteWidgets)
self.dataloader.bind(on_pageloaded=self.addPageWidgets)
self.dataloader.bind(on_newbegin=self.deleteAllWidgets)
self.dataloader = LClass(**lopts['options'])
if self.toolbar:
self.add_widget(self.toolbar)
if self.dataloader.widget:
@ -58,6 +56,30 @@ class BoxViewer(BoxLayout):
pos=self.resetCols)
self.viewContainer.bind(on_scroll_stop=self.on_scroll_stop)
def deleteAllWidgets(self,o):
self.viewContainer.clear_widgets()
def addPageWidgets(self,o,data):
widgets = []
recs = data['data']
dir = data['dir']
idx = 0
if dir == 'up':
recs.reverse()
idx = -1
print('addPageWidgets(),begin')
for r in recs:
self.showObject(widgets, r, index=idx)
print('addPageWidgets(),widgets=',len(widgets))
self.dataloader.bufferObjects(widgets)
x = self.dataloader.getLocater()
self.locater(x)
def deleteWidgets(self,o,data):
for w in data:
self.viewContainer.remove_widget(w)
def on_selected(self, o, v=None):
print('BoxViewer(): on_selected fired....')
@ -77,20 +99,29 @@ class BoxViewer(BoxLayout):
self.dataloader.loadPage(1)
self.initflag = True
def showObject(self, rec,**kw):
blocks = App.get_running_app().blocks
def showObject(self, holders, rec,index=0):
def doit(self,holders,idx,o,w):
print('showObject()...doit(),w=',w,o)
w.bind(on_press=self.select_record)
self.viewContainer.add_widget(w,index=idx)
holders.append(w)
def doerr(o,e):
print_exc()
print('showObject(),error=',e)
raise e
options = self.options['viewer'].copy()
options['options']['record'] = rec
options['options']['ancestor'] = self
options['options']['size_hint'] = None,None
options['options']['width'] = self.box_width
options['options']['height'] = self.box_height
w = blocks.widgetBuild(options, ancestor=self)
if w is None:
return None
w.bind(on_press=self.select_record)
self.viewContainer.add_widget(w,**kw)
return w
blocks = Factory.Blocks()
blocks.bind(on_built=partial(doit,self,holders,index))
blocks.bind(on_failed=doerr)
print('showObject():********here***********')
blocks.widgetBuild(options, ancestor=self)
print('showObject():********end ***********')
def on_scroll_stop(self,o,v=None):
if o.scroll_y <= 0.001:

View File

@ -46,6 +46,10 @@ form options
"""
uitypes = {
"str":{
"orientation":"horizontal",
"wclass":StrInput,
},
"string":{
"orientation":"horizontal",
"wclass":StrInput,
@ -54,6 +58,10 @@ uitypes = {
"orientation":"horizontal",
"wclass":Password,
},
"int":{
"orientation":"horizontal",
"wclass":IntegerInput,
},
"number":{
"orientation":"horizontal",
"wclass":IntegerInput,
@ -87,7 +95,7 @@ uitypes = {
"wclass":StrInput,
"options":{
"multiline":True,
"height":CSize(9),
"height":9,
}
},
"teleno":{
@ -111,9 +119,12 @@ class InputBox(BoxLayout):
self.labelwidth = self.form.options['labelwidth']
kwargs = {
"orientation":orientation,
"size_hint_y":None,
"height":height
}
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:
@ -121,7 +132,7 @@ class InputBox(BoxLayout):
kwargs['width'] = CSize(width)
super().__init__(**kwargs)
self.initflag = False
self.bind(on_size=self.setSize,
self.bind(size=self.setSize,
pos=self.setSize)
self.register_event_type("on_datainput")
self.register_event_type("on_ready")
@ -169,8 +180,8 @@ class InputBox(BoxLayout):
options = self.uidef.get('options',{}).copy()
options.update(self.options.get('uiparams',{}))
options['allow_copy'] = True
options['size_hint_y'] = None
options['height'] = CSize(3)
options['width'] = 1
options['height'] = 2.5
if self.options.get('tip'):
options['hint_text'] = i18n(self.options.get('tip'))
@ -217,6 +228,12 @@ class InputBox(BoxLayout):
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,
@ -241,23 +258,18 @@ class Form(BGColorBehavior, BoxLayout):
self.options = options
BoxLayout.__init__(self, orientation='vertical')
self.color_level = self.options.get('color_level', 0)
textcolor, bgcolor = getColors(self.color_level)
BGColorBehavior.__init__(self,bgcolor=bgcolor)
BGColorBehavior.__init__(self,color_level=self.color_level)
self.widget_ids = {}
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 = CSize(self.options.get('inputheight',3))
self.initflag = False
self.inputheight = self.options.get('inputheight',3)
self.init()
self.register_event_type('on_submit')
self.bind(size=self.on_size,
pos=self.on_size)
def init(self):
if self.initflag:
return
self.toolbar = Toolbar(ancestor=self,**self.options.get('toolbar',defaultToolbar()))
self.fsc = VResponsiveLayout(
self.inputwidth,
@ -279,7 +291,6 @@ class Form(BGColorBehavior, BoxLayout):
wid = blocks.getWidgetByIdPath(self,'__clear')
# wid = self.widget_ids['__clear']
wid.bind(on_press=self.on_clear_button)
self.initflag = True
def makeInputLink(self,o,v=None):
self.readiedInput += 1
@ -323,10 +334,6 @@ class Form(BGColorBehavior, BoxLayout):
for fw in self.fieldWidgets:
fw.clear()
def on_size(self,o, v=None):
self.init()
textcolor, self.bgcolor = getColors(self.color_level)
class StrSearchForm(BoxLayout):
def __init__(self,img_url=None,**options):
self.name = options.get('name','search_string')

View File

@ -28,6 +28,8 @@ class I18n:
if lang is None:
lang = self.lang
d = self.kvlang.get(lang,{})
if d is None:
return msg
return d.get(msg,msg)
def changeLang(self,lang):

View File

@ -41,18 +41,15 @@ class KivySizes:
return self.unitedSize(x,y=y,name=name)
def getScreenSize(self):
#root = App.get_running_app().root
#return root.width, root.height
return Window.width, Window.height
def getWindowPhysicalSize(self,w):
h_phy = float(w.height) / mm(1)
w_phy = float(w.width) / mm(1)
def getWindowPhysicalSize(self):
h_phy = float(Window.height) / mm(1)
w_phy = float(Window.width) / mm(1)
return w_phy, h_phy
def getScreenPhysicalSize(self):
return self.getWindowPhysicalSize(App.get_running_app().root)
return self.getWindowPhysicalSize(Window)
return self.getWindowPhysicalSize()
def isHandHold(self):
return min(self.getScreenPhysicalSize()) <= 75.0

View File

@ -1,4 +1,6 @@
from traceback import print_exc
from kivy.logger import Logger
from kivy.factory import Factory
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.popup import Popup
@ -35,21 +37,14 @@ class LoginForm(Popup):
def __init__(self):
super().__init__(size_hint=(0.8,0.8))
self.title = 'login'
self.initflag = False
self.bind(size=self.buildContent,
pos=self.buildContent)
self.buildContent(None,None)
self.register_event_type('on_setupuser')
def on_setupuser(self,o=None):
return
def buildContent(self,o,size):
print('build Content. ....')
if self.initflag:
return
print('build Content ....... ....')
self.initflag = True
app = App.get_running_app()
try:
self.content = app.blocks.widgetBuild(logformdesc)
except Exception as e:
Logger.info('login: Error %s', e)
self.content = Factory.Form(**logformdesc['options'])
self.content.bind(on_submit=self.on_submit)
def on_submit(self,o,userinfo):
@ -61,10 +56,7 @@ class LoginForm(Popup):
if userinfo.get('passwd',False):
userinfo['authmethod'] = 'password'
authinfo = app.serverinfo.encode(userinfo)
config = getConfig()
login_url = '%s%s' % (config.uihome, config.login_url)
x = app.hc.get(login_url)
print('login return=', x, login_url, authinfo)
self.dispatch('on_setupuser')
def on_cancel(self,o,v):
print('login() on_cancel fired .....')

75
kivyblocks/newwidget.py Normal file
View File

@ -0,0 +1,75 @@
from kivy.logger import Logger
from kivy.graphics import Color, Rectangle
from kivy.properties import ListProperty, NumericProperty
from kivy.factory import Factory
from kivy.uix.widget import Widget
from .color_definitions import getColors
class NewWidget(Widget):
bgcolor = ListProperty([])
fcolor = ListProperty([])
color_level = NumericProperty(-1)
def __init__(self, color_level=-1,bgcolor=[],fcolor=[],**kwargs):
super().__init__(**kwargs)
self.bgcolor = bgcolor
self.fcolor = fcolor
self.color_level = color_level
self.initColors()
self.bind(size=self.onSize_bgcolor_behavior,
pos=self.onSize_bgcolor_behavior)
def initColors(self):
if self.color_level != -1:
fcolor, bgcolor = getColors(self.color_level)
if self.bgcolor == []:
self.bgcolor = bgcolor
if self.fcolor == []:
self.fcolor = fcolor
def getParentColorLevel(self):
if not self.parent:
self.fcolor, self.bgcolor = getColors(self.color_level):
def on_color_level(self,o,v=None):
if bgcolor != [] and fcolor != []:
return
if color_level != -1:
fcolor, bgcolor = getColors(self.color_level)
if self.bgcolor == []:
self.bgcolor = bgcolor
if self.fcolor == []:
self.fcolor = fcolor
def on_fcolor(self,o,v=None):
self.color = self.fcolor
def on_parent(self,o,v=None):
if self.color_level != -1:
self.fcolor, self.bgcolor = getColors(self.color_level)
if self.color_level == -1:
self.setColorsFromAncestor()
def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'):
self.on_bgcolor()
else:
self.rect.pos = o.pos
self.rect.size = o.size
def on_bgcolor(self,o=None,v=None):
if self.bgcolor == []:
return
if self.canvas:
with self.canvas.before:
Color(*self.bgcolor)
self.rect = Rectangle(pos=self.pos,
size=self.size)
try:
Factory.unregister('Widget')
except:
pass
Factory.register('Widget',cls=NewWidget)

View File

@ -2,6 +2,7 @@ import traceback
import math
from kivy.logger import Logger
from kivy.event import EventDispatcher
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
@ -9,7 +10,7 @@ from kivy.app import App
from functools import partial
from appPublic.dictObject import DictObject
from appPublic.jsonConfig import getConfig
from .baseWidget import Text
from .baseWidget import Text, HTTPDataHandler
from .utils import CSize, absurl, alert
from .form import StrSearchForm
@ -28,37 +29,22 @@ class PagingButton(Button):
locater
filter
}
PageLoader load data in a page size once.
it fires two type of event
'on_newbegin':fire when start a new parameters loading series
'on_pageloaded':fire when a page data loads success
"""
class HttpLoader:
def __init__(self, url, method, params,page_rows=60):
self.page_rows = page_rows
self.url = url
self.method = method
self.params = params
def setParams(self,params, url=None, page_rows=60):
self.params = params
if url is not None:
self.url = url
self.page_rows = page_rows
def load(self, callback, errback=None):
hc = App.get_running_app().hc
x = hc(self.url,
method=self.method,
params=self.params,
callback=callback,
errback=errback)
class PageLoader:
def __init__(self, **options):
class PageLoader(EventDispatcher):
def __init__(self,target=None, **options):
self.loading = False
self.target = target
self.options = options
self.filter = None
if options.get('filter'):
self.filter = StrSearchForm(**options['filter'])
self.filter.bind(on_submit=self.do_search)
self.params = options.get('params',{})
self.method = options.get('method','GET')
self.url = options.get('dataurl')
@ -66,14 +52,34 @@ class PageLoader:
self.total_cnt = 0
self.total_page = 0
self.curpage = 0
Logger.info('kivyblocks: method=%s,type=%s',str(self.method),type(self.method))
self.loader = HttpLoader(self.url, self.method, self.params )
self.dir = 'down'
self.register_event_type('on_newbegin')
self.register_event_type('on_pageloaded')
def getLocater(self):
x = 1 / self.MaxbufferPage
if self.dir != 'down':
x = 1 - x
print('getLocater(),x=%f,dir=%s,self.curpage=%d' % (x,self.dir,self.curpage))
return x
def on_newbegin(self):
pass
def on_pageloaded(self,d):
"""
d={
page:
data:[]
dir:'up' or 'down'
}
"""
pass
def do_search(self,o,params):
print('PageLoader().do_search(), on_submit handle....',params)
self.clearer()
self.dispatch('on_newbegin')
self.params.update(params)
self.loader.setParams(self.params)
self.loadPage(1)
def calculateTotalPage(self):
@ -94,21 +100,19 @@ class PageLoader:
self.curpage = 1
self.loadPage(self.curpage)
def setDir(self):
pages = self.objectPages.keys()
if len(pages)==0:
self.dir = 'down'
return
if self.curpage >= max(self.objectPages.keys()):
self.dir = 'down'
else:
self.dir = 'up'
def show_page(self,o,d):
p = (self.curpage - 1) * self.page_rows + 1
for r in d['rows']:
r['__posInSet__'] = p
p += 1
self.total_cnt = d['total']
self.calculateTotalPage()
d = {
"page":self.curpage,
"dir":self.dir,
"data":d['rows']
}
self.dispatch('on_pageloaded',d)
def httperror(self,o,e):
traceback.print_exc()
@ -117,6 +121,12 @@ class PageLoader:
def loadPage(self,p):
if not self.url:
raise Exception('dataurl must be present:options=%s' % str(options))
if self.curpage > p:
self.dir = 'up'
elif self.curpage == p:
self.dir = 'reload'
else:
self.dir = 'down'
self.curpage = p
self.loading = True
params = self.params.copy()
@ -124,9 +134,11 @@ class PageLoader:
"page":self.curpage,
"rows":self.page_rows
})
url = absurl(self.url,self.target.parenturl)
self.loader.setParams(params,url=url)
self.loader.load(self.show_page,self.httperror)
realurl = absurl(self.url,self.target.parenturl)
loader = HTTPDataHandler(realurl,params=params)
loader.bind(on_success=self.show_page)
loader.bind(on_failed=self.httperror)
loader.handle()
"""
{
@ -139,22 +151,25 @@ class PageLoader:
method,
filter
}
events:
'on_bufferraise': erase
"""
class RelatedLoader(PageLoader):
def __init__(self, **options):
super().__init__(**options)
self.adder = options.get('adder')
self.locater = options.get('locater',None)
self.remover = options.get('remover')
self.clearer = options.get('clearer')
self.target = options.get('target')
self.objectPages = {}
self.totalObj = 0
self.MaxbufferPage = 3
self.locater = 1/self.MaxbufferPage
if self.filter:
self.widget = self.filter
else:
self.widget = None
self.register_event_type('on_deletepage')
def on_deletepage(self,d):
pass
def setPageRows(self,row_cnt):
if self.total_cnt != 0:
@ -171,42 +186,21 @@ class RelatedLoader(PageLoader):
self.deleteBuffer(p)
def deleteBuffer(self,page):
for w in self.objectPages[page]:
self.remover(w)
d = self.objectPages[page]
self.dispatch('on_deletepage',d)
self.totalObj -= len(self.objectPages[page])
del self.objectPages[page]
def bufferObjects(self,objects):
self.objectPages[self.curpage] = objects
def show_page(self,o,data):
super().show_page(o,data)
if self.objectPages.get(self.curpage):
self.deleteBuffer(self.curpage)
else:
self.setDir()
self.doBufferMaintain()
self.total_cnt = data['total']
self.calculateTotalPage()
widgets = []
rows = data['rows']
if self.dir == 'up':
rows.reverse()
for r in rows:
if self.dir == 'up':
w = self.adder(r,index=self.totalObj)
else:
w = self.adder(r)
widgets.append(w)
self.totalObj += 1
self.objectPages[self.curpage] = widgets
pages = len(self.objectPages.keys())
loc = 1.0 / float(pages)
if self.locater:
if pages == 1:
self.locater(1)
elif self.dir == 'up':
self.locater(1 - loc)
else:
self.locater(loc)
self.totalObj += len(data['rows'])
super().show_page(o,data)
self.loading = False
print('buffer pages=',len(self.objectPages.keys()),'pages=',self.objectPages.keys())
@ -218,12 +212,12 @@ class RelatedLoader(PageLoader):
if len(pages) < 1:
return
self.curpage = min(pages)
if self.curpage <= 1:
page = min(pages)
if page <= 1:
return
self.curpage -= 1
self.loadPage(self.curpage)
page -= 1
self.loadPage(page)
def loadNextPage(self):
if self.loading:
@ -231,11 +225,11 @@ class RelatedLoader(PageLoader):
pages = self.objectPages.keys()
if len(pages) == 0:
return
self.curpage = max(pages)
if self.curpage>=self.total_page:
page = max(pages)
if page>=self.total_page:
return
self.curpage += 1
self.loadPage(self.curpage)
page += 1
self.loadPage(page)
"""
{

41
kivyblocks/register.py Normal file
View File

@ -0,0 +1,41 @@
from kivy.utils import platform
from .baseWidget import *
from .tree import Tree, TextTree, PopupMenu
from .toolbar import ToolPage, Toolbar
from .dg import DataGrid
from .vplayer import VPlayer
from .form import Form, StrSearchForm
from .boxViewer import BoxViewer
from .pagescontainer import PageContainer
r = Factory.register
r('PageContainer', PageContainer)
r('BoxViewer', BoxViewer)
r('Form', Form)
r('StrSearchForm', StrSearchForm)
r('VPlayer', VPlayer)
r('DataGrid', DataGrid)
r('Toolbar', Toolbar)
r('ToolPage',ToolPage)
r('HTTPDataHandler',HTTPDataHandler)
r('Text',Text)
r('ScrollWidget',ScrollWidget)
r('BinStateImage',BinStateImage)
r('JsonCodeInput',JsonCodeInput)
r('FloatInput',FloatInput)
r('IntegerInput',IntegerInput)
r('StrInput',StrInput)
r('SelectInput',SelectInput)
r('BoolInput',BoolInput)
r('AmountInput',AmountInput)
r('Messager',Messager)
r('Bar',Bar)
r('LoginForm',LoginForm)
r('PressableImage', PressableImage)
r('PressableLabel', PressableLabel)
r('Tree',Tree)
r('TextTree',TextTree)
r('PopupMenu',PopupMenu)
if platform == 'android':
r('PhoneButton',PhoneButton)
r('AWebView',AWebView)

35
kivyblocks/tab.py Normal file
View File

@ -0,0 +1,35 @@
"""
{
"widgettype":"BLKTabbedPanel",
"tab_pos":"top_left",
"color_level":0,
"width",
"height",
"size_hint",
"tabs":[
{
"text":"tab1",
"content":{
"widgettype":"urlwidegt",
"url":"reggtY",
}
},
{
}
]
}
"""
class BLKTabItem(BGColorBehavior, TabbedPanelItem):
def __init__(self,parent,color_level=0,text="",content={}):
self.parent=parent
self.color_level = color_level
class BLKTabedPanel(BGColorBehavior, TabbedPanel):
def __init__(self,color_level=0, tabs=[], **kwargs):
self.tabs_desc = tabs
self.color_level = color_level
TabbedPanel.__init__(**kwargs)
BGColorBehavior.__init__(self)
class

View File

@ -6,9 +6,7 @@ from kivy.event import EventDispatcher
from kivy.clock import Clock
from kivy.app import App
from .login import LoginForm
class NeedLogin(Exception):
pass
from .utils import NeedLogin, InsufficientPrivilege, HTTPError
class ThreadCall(Thread,EventDispatcher):
def __init__(self,target, args=(), kwargs={}):
@ -95,7 +93,11 @@ class HttpClient:
app = App.get_running_app()
domain = self.url2domain(url)
sessionid = hostsessions.get(domain,None)
print('hostsessions=', hostsessions, 'sessionid=',sessionid, 'domain=',domain)
print('hostsessions=', hostsessions,
'sessionid=',sessionid,
'domain=',domain,
'url=',url
)
if sessionid:
headers.update({'session':sessionid})
elif app.getAuthHeader():
@ -128,10 +130,16 @@ class HttpClient:
except:
return resp.text
if resp.status_code == 401:
print('NeedLogin:',url)
raise NeedLogin
print('Error', url, method, params, resp.status_code,type(resp.status_code))
raise Exception('error:%s' % url)
if resp.status_code == 403:
raise InsufficientPrivilege
print('Error', url, method,
params, resp.status_code,
type(resp.status_code))
raise HTTPError(resp.status_code)
def __call__(self,url,method="GET",params={},headers={},files={},callback=None,errback=None):
def cb(t,resp):
@ -142,10 +150,8 @@ class HttpClient:
resp = self.webcall(url, method=method,
params=params, files=files, headers=headers)
return cb(None,resp)
except NeedLogin as e:
lf = LoginForm()
lf.open()
return None
except Exception as e:
raise e
except Exception as e:
if errback is not None:
errback(e)

View File

@ -42,7 +42,7 @@ class Tool(ButtonBehavior, BGColorBehavior, BoxLayout):
ButtonBehavior.__init__(self)
BoxLayout.__init__(self,
size_hint_y=None)
BGColorBehavior.__init__(self)
BGColorBehavior.__init__(self,color_level=ancestor.color_level)
self.bl = BoxLayout(orientation='vertical',
size_hint_y=None)
self.add_widget(self.bl)
@ -62,7 +62,8 @@ class Tool(ButtonBehavior, BGColorBehavior, BoxLayout):
tsize = CSize(self.opts.text_size)
label = self.opts.label or self.opts.name
self.lbl = I18nText(otext=label,
self.lbl = I18nText(color_level=self.ancestor.color_level,
otext=label,
font_size=int(tsize),
text_size=(CSize(len(label)), tsize),
height=tsize,
@ -71,24 +72,21 @@ class Tool(ButtonBehavior, BGColorBehavior, BoxLayout):
)
self.bl.add_widget(self.lbl)
self.height = (size + tsize)*1.1
self.lbl.color, self.bgcolor = getColors(self.ancestor.color_level,
selected=False)
self.lbl.bgcolor = self.bgcolor
def on_size(self,obj,size):
Logger.info('toolbar: Tool() on_size fired')
self.lbl.color, self.bgcolor = getColors(self.ancestor.color_level,
selected=False)
self.lbl.bgcolor = self.bgcolor
#self.lbl.color, self.bgcolor = getColors(self.ancestor.color_level,
# selected=False)
#self.lbl.bgcolor = self.bgcolor
def on_press(self):
print('Tool(). pressed ...............')
def setActive(self,flag):
text_color, self.bgcolor = getColors(self.ancestor.color_level,
selected=flag)
self.lbl.bgcolor = self.bgcolor
self.lbl.color = text_color
if flag:
self.selected()
else:
self.unselected()
"""
@ -106,11 +104,15 @@ toolbar options
]
}
"""
class Toolbar(GridLayout):
class Toolbar(BGColorBehavior, GridLayout):
def __init__(self, ancestor=None,**opts):
self.opts = DictObject(**opts)
self.tool_widgets={}
super().__init__(cols = len(self.opts.tools))
GridLayout.__init__(self,cols = len(self.opts.tools))
color_level = 0
if isinstance(ancestor, BGColorBehavior):
color_level = ancestor.color_level + 1
BGColorBehavior.__init__(self,color_level=color_level)
self.size_hint = (1,None)
first = True
for opt in self.opts.tools:
@ -175,9 +177,9 @@ class ToolPage(BGColorBehavior, BoxLayout):
orient = 'vertical'
else:
orient = 'horizontal'
color_level=self.opts.color_level or 0
BoxLayout.__init__(self,orientation=orient)
BGColorBehavior.__init__(self)
BGColorBehavior.__init__(self,color_level=color_level)
self.content = None
self.toolbar = None
self.init()
@ -190,7 +192,6 @@ class ToolPage(BGColorBehavior, BoxLayout):
self.toolbar.width = x
self.content.width = x
self.content.height = y - self.toolbar.height
self.color, self.bgcolor = getColors(self.color_level)
def showPage(self,obj):
self._show_page(obj.opts)
@ -211,8 +212,6 @@ class ToolPage(BGColorBehavior, BoxLayout):
t.img_src = absurl(t.img_src,parenturl)
opts = self.opts
self.color_level = self.opts.color_level or 0
self.color, self.bgcolor = getColors(self.color_level)
self.toolbar = Toolbar(ancestor=self, **self.opts)
if self.opts.tool_at in ['top','left']:
self.add_widget(self.toolbar)

View File

@ -285,7 +285,7 @@ class Tree(BGColorBehavior, ScrollWidget):
def __init__(self,**options):
self.color_level = options.get('color_level',0)
ScrollWidget.__init__(self)
BGColorBehavior.__init__(self)
BGColorBehavior.__init__(self,color_level=self.color_level)
self.options = DictObject(**options)
self.nodes = []
self.initflag = False
@ -377,21 +377,16 @@ class Tree(BGColorBehavior, ScrollWidget):
self.nodes = [ i for i in self.nodes if i != node ]
class TextContent(PressableLabel):
def __init__(self,level=0,**options):
PressableLabel.__init__(self,**options)
def selected(self):
pass
def unselected(self):
pass
def __init__(self,color_level=0,**options):
PressableLabel.__init__(self,color_level=color_level,**options)
class TextTreeNode(TreeNode):
def buildContent(self):
txt = self.data.get(self.treeObj.options.textField,
self.data.get(self.treeObj.options.idField,'defaulttext'))
self.content = TextContent( text=txt,
self.content = TextContent(color_level=self.treeObj.color_level,
text=txt,
size_hint=(None,None),
font_size=CSize(1),
text_size=CSize(len(txt)-1,1),
@ -412,18 +407,12 @@ class TextTreeNode(TreeNode):
self.treeObj.select_row(self)
def selected(self):
Logger.info('content selected ........')
color, bgcolor = getColors(self.treeObj.color_level,
selected=True)
self.content.bgcolor = bgcolor
self.content.color = color
if hasattr(self.content,'selected'):
self.content.selected()
def unselected(self):
Logger.info('content unselected ........')
color, bgcolor = getColors(self.treeObj.color_level,
selected=False)
self.content.bgcolor = bgcolor
self.content.color = color
if hasattr(self.content,'unselected'):
self.content.unselected()
class TextTree(Tree):
def __init__(self,**options):

View File

@ -1,4 +1,5 @@
import os
from traceback import print_exc
from kivy.app import App
from appPublic.jsonConfig import getConfig
from kivy.uix.popup import Popup
@ -11,6 +12,23 @@ from appPublic.dictObject import DictObject
from .kivysize import KivySizes
class NeedLogin(Exception):
pass
class InsufficientPrivilege(Exception):
pass
class HTTPError(Exception):
def __init__(self,resp_code):
self.resp_code = resp_code
Exception.__init__(self)
def __expr__(self):
return f'Exception:return code={self.resp_code}'
def __str__(self):
return f'Exception:return code={self.resp_code}'
alert_widget= None
def blockImage(name):
@ -81,6 +99,9 @@ def alert(text,title='alert'):
x = x + ':hh'
alert_widget.title = str(title) + x
alert_widget.open()
print('************')
print_exc()
print('************')
def StrConvert(s):
if not s.startswith('py::'):

View File

@ -47,21 +47,31 @@ class StrInput(TextInput):
kv = {}
a = {
"allow_copy":True,
"font_size":1,
"password":False,
"multiline":False,
"halign":"left",
"hint_text":"",
"size_hint_y":None,
"width":20,
"height":2.5
}
if kv.get('tip'):
a['hint_text'] = kv['tip']
a.update(kv)
a['width'] = CSize(kv.get('width',20))
a['height'] = CSize(kv.get('height',2.5))
# a.update(kv)
w = kv.get('width',20)
h = kv.get('height',2.5)
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)
a['font_size'] = CSize(kv.get('font_size',1))
a['password'] = kv.get('password',False)
a['multiline'] = kv.get('multiline',False)
Logger.info('TextInput:a=%s,h=%d,w=%d,CSize(1)=%d',a,h,w,CSize(1))
super(StrInput,self).__init__(**a)
self.old_value = None
self.register_event_type('on_changed')
@ -149,13 +159,16 @@ class MyDropDown(DropDown):
def __init__(self,**kw):
super(MyDropDown,self).__init__()
self.options = kw
self.textField = kw.get('textField',None)
self.valueField = kw.get('valueField',None)
if kw.get('url') is not None:
self.url = kw.get('url')
self.textField = kw.get('textField','text')
self.valueField = kw.get('valueField','value')
self.loadSelectItems()
def loadSelectItems(self):
if self.options.get('url') is not None:
self.url = self.options.get('url')
self.setDataByUrl(self.url)
else:
self.si_data = kw.get('data')
self.si_data = self.options.get('data')
self.setData(self.si_data)
self.bind(on_select=lambda instance, x: self.selectfunc(x))
@ -179,12 +192,23 @@ class MyDropDown(DropDown):
def setData(self,data):
self.si_data = data
self.clear_widgets()
w = self.options.get('width',10)
h = self.options.get('height',2.5)
a = {}
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)
a['font_size'] = CSize(self.options.get('font_size',1))
for d in data:
dd = (d[self.valueField],d[self.textField])
b = Button(text=d[self.textField],font_size=CSize(1),
size_hint=(None,None),
width=CSize(10),
height=CSize(2.5))
b = Button(text=d[self.textField],**a)
setattr(b,'kw_data',dd)
b.bind(on_release=lambda btn: self.select(btn.kw_data))
self.add_widget(b)
@ -205,10 +229,22 @@ class MyDropDown(DropDown):
class SelectInput(BoxLayout):
def __init__(self,**kw):
a={}
w = kw.get('width',10)
h = kw.get('height',2.5)
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'] = h
super(SelectInput,self).__init__(orientation='horizontal',\
size_hint=(None,None),
width=CSize(kw.get('width',10)),
height=CSize(kw.get('height',2.5)))
**a)
self.tinp = StrInput(size_hint_y=None,height=kw.get('height',2.5))
self.tinp.readonly = True
newkw = {}