From 70256ae9def41a0d900f47c3929de268ea19b243 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 4 Nov 2022 03:17:11 -0400 Subject: [PATCH] bugfix --- kivyblocks/blocks.py | 1641 +++++++++++++++++++------------------- kivyblocks/dataloader.py | 375 ++++----- kivyblocks/paging.py | 648 +++++++-------- 3 files changed, 1337 insertions(+), 1327 deletions(-) diff --git a/kivyblocks/blocks.py b/kivyblocks/blocks.py index bce72f2..b789073 100755 --- a/kivyblocks/blocks.py +++ b/kivyblocks/blocks.py @@ -1,821 +1,820 @@ -import os -import sys -import codecs -# import ujson as json -try: - import ujson as json -except: - import json -from traceback import print_exc - -from functools import partial - -from appPublic.dictExt import dictExtend -from appPublic.folderUtils import ProgramPath -from appPublic.dictObject import DictObject -from appPublic.Singleton import SingletonDecorator, GlobalEnv -from appPublic.datamapping import keyMapping -from appPublic.registerfunction import RegisterFunction - -from kivy.logger import Logger -from kivy.config import Config -from kivy.metrics import sp,dp,mm -from kivy.core.window import WindowBase, Window -from kivy.properties import BooleanProperty -from kivy.uix.widget import Widget -from kivy.uix.modalview import ModalView -from kivy.app import App -from kivy.factory import Factory -from kivy.uix.video import Video -from .utils import * -from .newvideo import Video -from .orientationlayout import OrientationLayout -from .threadcall import HttpClient -from .register import * -from .script import Script, set_script_env - -class WidgetNotFoundById(Exception): - def __init__(self, id): - super().__init__() - self.idstr = id - - def __str__(self): - return "Widget not found by id:" + self.idstr + ':' - - def __expr__(self): - return str(self) - -class ClassMethodNotFound(Exception): - def __init__(self,k,m): - super().__init__() - self.kname = k - self.mname = m - - def __str__(self): - s = 'Method(%s) not found in class(%s)' % (self.mname, - str(self.kname.__classname__)) - return s - - def __expr__(self): - return self.__str__() - - -class NotExistsObject(Exception): - def __init__(self,name): - super().__init__() - self.name = name - - def __str__(self): - s = 'not exists widget(%s)' % self.name - return s - - def __expr__(self): - return self.__str__() - -class ArgumentError(Exception): - def __init__(self,argument,desc): - super().__init__() - self.argument = argument - self.desc = desc - - def __str__(self): - s = 'argument(%s) missed:%s' % (self.argument,self.desc) - return s - - def __expr__(self): - return self.__str__() - - -class NotRegistedWidget(Exception): - def __init__(self, name:str): - super().__init__() - self.widget_name = name - - def __str__(self): - s = 'not reigsted widget(%s)' % self.name - return s - - def __expr__(self): - return self.__str__() - -def registerWidget(name:str,widget): - globals()[name] = widget - - -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') - self.env = GlobalEnv() - config = getConfig() - if config.script_root: - self.script = Script(config.script_root) - else: - self.script = Script(config.workdir) - - def set(self, k:str, v): - self.env[k] = v - - def register_widget(self, name:str, widget:Widget): - globals()[name] = widget - - def buildAction(self, widget:Widget, desc): - conform_desc = desc.get('conform') - blocks = Blocks() - if not conform_desc: - return partial(blocks.uniaction, widget, desc) - func =partial(blocks.conform_action,widget, desc) - return func - - def eval(self, s:str, l:dict): - g = {} - forbidens = [ - "os", - "sys", - "codecs", - "json", - ] - - for k,v in globals().copy().items(): - if k not in forbidens: - g[k] = v - - """ - g['__builtins__'] = globals()['__builtins__'] - g['__builtins__']['__import__'] = None - g['__builtins__']['__loader__'] = None - g['__builtins__']['open'] = None - """ - g.update(self.env) - return eval(s,g,l) - - def getUrlData(self, url:str, method:str='GET', - params:dict={}, files:dict={}, - callback=None, - errback=None,**kw): - - if url is None: - if errback: - errback(None,Exception('url is None')) - else: - return None - - if url.startswith('file://'): - return self.script.dispatch(url, **params) - elif url.startswith('http://') or url.startswith('https://'): - try: - hc = HttpClient() - resp=hc(url,method=method,params=params,files=files) - # print('Blocks.py :resp=',resp) - return resp - except Exception as e: - print_exc() - if errback: - return errback(None,e) - return None - else: - config = getConfig() - url = config.uihome + url - return self.getUrlData(url,method=method, - params=params, - files=files, - **kw) - - def strValueExpr(self,s:str,localnamespace:dict={}): - if not s.startswith('py::'): - return s - s = s[4:] - try: - v = self.eval(s,localnamespace) - return v - except Exception as e: - print('Exception .... ',e,'script=',s) - print_exc() - return s - - def arrayValueExpr(self,arr:list,localnamespace:dict={}) -> list: - d = [] - for v in arr: - if type(v) == type(''): - d.append(self.strValueExpr(v,localnamespace)) - continue - if type(v) == type([]): - d.append(self.arrayValueExpr(v,localnamespace)) - continue - if type(v) == type({}): - d.append(self.dictValueExpr(v,localnamespace)) - continue - if type(v) == type(DictObject): - d.append(self.dictValueExpr(v,localnamespace)) - continue - d.append(v) - return d - - def dictValueExpr(self,dic:dict,localnamespace:dict={}) -> dict: - d = {} - for k,v in dic.items(): - if type(v) == type(''): - d[k] = self.strValueExpr(v,localnamespace) - continue - if type(v) == type([]): - d[k] = self.arrayValueExpr(v,localnamespace) - continue - if type(v) == type({}): - d[k] = self.dictValueExpr(v,localnamespace) - continue - if type(v) == type(DictObject): - d[k] = self.dictValueExpr(v,localnamespace) - continue - d[k] = v - return d - def valueExpr(self,obj,localnamespace:dict={}): - if type(obj) == type(''): - return self.strValueExpr(obj,localnamespace) - if type(obj) == type([]): - return self.arrayValueExpr(obj,localnamespace) - if type(obj) == type({}): - return self.dictValueExpr(obj,localnamespace) - if isinstance(obj,DictObject): - return self.dictValueExpr(obj,localnamespace) - return obj - - def w_build(self,desc) -> Widget: - widgetClass = desc.get('widgettype',None) - if not widgetClass: - Logger.info("Block: w_build(), desc invalid", desc) - raise Exception(desc) - - widgetClass = desc['widgettype'] - opts = self.valueExpr(desc.get('options',{}).copy()) - widget = None - try: - klass = Factory.get(widgetClass) - widget = klass(**opts) - except Exception as e: - print('Error:',widgetClass,'contructon error') - print_exc() - raise NotExistsObject(widgetClass) - - if desc.get('id'): - id = desc.get('id') - if id.startswith('app.'): - app = App.get_running_app() - id = id[4:] - setattr(app, id, widget) - if id.startswith('root.'): - app = App.get_running_app() - id = id[5:] - setattr(app.root, id, widget) - - if '.' in id: - Logger.info('widget id(%s) can not contain "."', id) - else: - widget.widget_id = id - - widget.build_desc = desc - self.build_attributes(widget, desc) - self.build_rest(widget, desc) - self.buildBinds(widget, desc) - return widget - - def build_attributes(self, widget:Widget,desc,t=None): - excludes = ['widgettype','options','subwidgets','binds'] - for k,v in [(k,v) for k,v in desc.items() if k not in excludes]: - if isinstance(v,dict) and v.get('widgettype'): - b = Blocks() - v = self.valueExpr(v, localnamespace={'self':widget}) - w = b.widgetBuild(v) - if hasattr(widget,k): - aw = getattr(widget,k) - if isinstance(aw,Layout): - aw.add_widget(w) - continue - setattr(widget,k,w) - continue - setattr(widget,k,self.valueExpr(v,\ - localnamespace={'self':widget})) - - def build_rest(self, widget:Widget,desc,t=None): - self.subwidget_total = len(desc.get('subwidgets',[])) - self.subwidgets = [ None for i in range(self.subwidget_total)] - pos = 0 - for pos,sw in enumerate(desc.get('subwidgets',[])): - b = Blocks() - if isinstance(sw, str): - # w = Blocks.getWidgetById(sw, from_widget=widget) - w = Blocks.findWidget(sw, from_widget=widget) - if w: - widget.add_widget(w) - continue - - kw = self.valueExpr(sw.copy(), - localnamespace={'self':widget}) - w = b.widgetBuild(kw) - if w: - widget.add_widget(w) - - - def buildBinds(self, widget:Widget, desc:dict): - for b in desc.get('binds',[]): - kw = self.valueExpr(b.copy(), \ - localnamespace={'self':widget}) - self.buildBind(widget,kw) - - def buildBind(self, widget:Widget, desc:dict): - wid = desc.get('wid','self') - # w = Blocks.getWidgetById(desc.get('wid','self'),from_widget=widget) - w = Blocks.findWidget(desc.get('wid','self'),from_widget=widget) - if not w: - Logger.info('Block: id(%s) %s',desc.get('wid','self'), - 'not found via Blocks.getWidgetById()') - return - event = desc.get('event') - if event is None: - Logger.info('Block: binds desc miss event, desc=%s',str(desc)) - return - f = self.buildAction(widget,desc) - if f is None: - Logger.info('Block: get a null function,%s',str(desc)) - return - w.bind(**{event:f}) - - def multipleAction(self, widget:Widget, desc, *args): - desc1 = {k:v for k, v in desc.items() if k != 'actions'} - mydesc = desc1.copy() - for a in desc['actions']: - new_desc = mydesc.copy() - new_desc.update(a) - self.uniaction(widget,new_desc, *args) - - def conform_action(self, widget:Widget, desc, *args): - conform_desc = desc.get('conform') - blocks = Blocks() - if not conform_desc: - blocks.uniaction(widget, desc,*args, **kw) - return - w = blocks.widgetBuild({ - "widgettype":"Conform", - "options":conform_desc - }) - w.bind(on_conform=partial(blocks.uniaction, widget, desc)) - w.open() - - def uniaction(self, widget:Widget, desc, *args): - acttype = desc.get('actiontype') - if acttype=='blocks': - return self.blocksAction(widget,desc, *args) - if acttype=='urlwidget': - return self.urlwidgetAction(widget,desc, *args) - if acttype == 'registedfunction': - return self.registedfunctionAction(widget,desc, *args) - if acttype == 'script': - return self.scriptAction(widget, desc, *args) - if acttype == 'method': - return self.methodAction(widget, desc, *args) - if acttype == 'event': - return self.eventAction(widget, desc, *args) - if acttype == 'multiple': - return self.multipleAction(widget, desc, *args) - - alert("actiontype(%s) invalid" % acttype,title='error') - - def eventAction(self, widget:Widget, desc, *args): - target = self.get_target(widget, desc) - event = desc.get('dispatch_event') - if not event: - Logger.info('Block: eventAction():desc(%s) miss dispatch_event', - str(desc)) - return - params = desc.get('params',{}) - d = self.getActionData(widget,desc, *args) - if d: - params.update(d) - try: - target.dispatch(event, params) - except Exception as e: - Logger.info(f'Block: eventAction():dispatch {event} error') - print_exc() - return - - def get_target(self, widget:Widget, desc): - if not desc.get('target'): - return None - # return Blocks.getWidgetById(desc.get('target'),from_widget=widget) - return Blocks.findWidget(desc.get('target'),from_widget=widget) - - def blocksAction(self, widget:Widget, desc, *args): - target = self.get_target(widget, desc) - add_mode = desc.get('mode','replace') - opts = desc.get('options').copy() - d = self.getActionData(widget,desc, *args) - p = opts.get('options',{}).copy() - if d: - p.update(d) - opts['options'] = p - def doit(target:Widget, add_mode:str, o, w:Widget): - if isinstance(w, ModalView): - return - - if target and not w.parent: - if add_mode == 'replace': - target.clear_widgets() - target.add_widget(w) - - def doerr(o,e): - Logger.info('Block: blocksAction(): desc=%s widgetBuild error' - ,str(desc)) - raise e - - b = Blocks() - b.bind(on_built=partial(doit,target,add_mode)) - b.bind(on_failed=doerr) - b.widgetBuild(opts) - - def urlwidgetAction(self, widget:Widget, desc, *args): - target = self.get_target(widget, desc) - add_mode = desc.get('mode','replace') - opts = desc.get('options', {}).copy() - p1 = opts.get('params',{}) - p = {} - if p1: - p.update(p1) - if len(args) >= 1 and isinstance(args[0],dict): - p.update(args[0]) - d = self.getActionData(widget, desc, *args) - if d: - p.update(d) - opts['params'] = p - d = { - 'widgettype' : 'urlwidget', - 'options': opts - } - - def doit(target:Widget, add_mode:str, o, w:Widget): - if isinstance(w, ModalView): - return - - if target and not w.parent: - if add_mode == 'replace': - target.clear_widgets() - target.add_widget(w) - - def doerr(o,e): - Logger.info('Block: urlwidgetAction(): desc=%s widgetBuild error' - ,str(desc)) - - b = Blocks() - b.bind(on_built=partial(doit,target,add_mode)) - b.bind(on_failed=doerr) - b.widgetBuild(d) - - def getActionData(self, widget:Widget, desc, *args): - data = {} - rtdesc = self.build_rtdesc(desc) - if rtdesc: - rt = self.get_rtdata(widget, rtdesc, *args) - if rt: - data.update(rt) - if desc.get('keymapping'): - data = keyMapping(data, desc.get('keymapping')) - Logger.info('getActionData():rtdesc=%s, data=%s', rtdesc, data) - return data - - def registedfunctionAction(self, widget:Widget, desc, *args): - target = self.get_target(widget, desc) - rf = RegisterFunction() - name = desc.get('rfname') - func = rf.get(name) - if func is None: - Logger.info('Block: desc=%s rfname(%s) not found', - str(desc), name) - raise Exception('rfname(%s) not found' % name) - - params = desc.get('params',{}).copy() - d = self.getActionData(widget,desc, *args) - if d: - params.update(d) - func(target, *args, **params) - - def scriptAction(self, widget:Widget, desc, *args): - script = desc.get('script') - if not script: - Logger.info('Block: scriptAction():desc(%s) target not found', - str(desc)) - return - target = self.get_target(widget, desc) - d = self.getActionData(widget,desc, *args) - ns = { - "self":target, - "args":args, - "kwargs":d - } - if d: - ns.update(d) - try: - self.eval(script, ns) - except Exception as e: - print_exc() - print(e) - - def build_rtdesc(self, desc): - rtdesc = desc.get('rtdata') - if not rtdesc: - if desc.get('datawidget'): - rtdesc = {} - rtdesc['target'] = desc['datawidget'] - if desc.get('datascript'): - rtdesc['script'] = desc.get('datacript') - else: - rtdesc['method'] = desc.get('datamethod', 'getValue') - rtdesc['kwargs'] = desc.get('datakwargs', {}) - return rtdesc - - def get_rtdata(self, widget:Widget, desc, *args): - """ - desc descript follow attributes - { - "target", a target name from widget - "method", a method name, default is 'getValue', - "script", script to handle the data - "kwargs":" a dict arguments, default is {} - } - """ - - if desc is None or desc == {}: - print('get_rtdata():desc is None') - return {} - kwargs = desc.get('kwargs', {}) - if not isinstance(kwargs, dict): - kwargs = {} - w = None - if len(args) > 0: - w = args[0] - target = desc.get('target') - if target: - # w = Blocks.getWidgetById(target, from_widget=widget) - w = Blocks.findWidget(target, from_widget=widget) - if w is None: - print('get_rtdata():w is None', desc) - return {} - script = desc.get('script') - if script: - ns = { - "self":w, - "args":args, - "kwargs":kwargs - } - d = self.eval(script, ns) - return d - - method = desc.get('method', 'getValue') - if not hasattr(w, method): - print('get_rtdata():method is None', desc, type(w)) - return {} - f = getattr(w, method) - try: - r = f(**kwargs) - if isinstance(r, dict): - return r - if isinstance(r, DictObject): - return r.to_dict() - print('get_rtdata():method return is not dict', desc, 'ret=', r, type(r)) - return {} - except: - print_exc() - return {} - - def methodAction(self, widget:Widget, desc, *args): - method = desc.get('method') - target = self.get_target(widget, desc) - if target is None: - Logger.info('Block: methodAction():desc(%s) target not found', - str(desc)) - return - if method is None: - Logger.info('Block: methodAction():desc(%s) method not found', - str(desc)) - return - if hasattr(target,method): - f = getattr(target, method) - kwargs = desc.get('options',{}).copy() - d = self.getActionData(widget,desc, *args) - if d: - kwargs.update(d) - f(*args, **kwargs) - else: - alert('%s method not found' % method) - - def widgetBuild(self, desc): - """ - desc format: - { - widgettype:, - id:widget id, - options:{} - subwidgets:[ - ] - binds:[ - ] - } - """ - def doit(desc): - if isinstance(desc,DictObject): - desc = desc.to_dict() - if not isinstance(desc,dict): - Logger.info('Block: desc(%s) must be a dict object(%s)', - desc,type(desc)) - return None - - # desc = self.valueExpr(desc) - try: - widget = self.w_build(desc) - self.dispatch('on_built',widget) - if hasattr(widget,'ready'): - widget.ready = True - return widget - except Exception as e: - print_exc() - self.dispatch('on_failed',e) - return None - - if isinstance(desc,DictObject): - desc = desc.to_dict() - if not isinstance(desc, dict): - print('widgetBuild1: desc must be a dict object', - desc,type(desc)) - self.dispatch('on_failed',Exception('miss url')) - return - - widgettype = desc.get('widgettype') - while widgettype == "urlwidget": - opts = desc.get('options',{}).copy() - extend = desc.get('extend') - addon = None - if desc.get('extend'): - addon = desc.get('extend').copy() - url = opts.get('url') - if url is None: - self.dispatch('on_failed',Exception('miss url')) - return - - if opts.get('url'): - del opts['url'] - rtdesc = self.build_rtdesc(opts) - if rtdesc: - rtdata = self.get_rtdata(None, rtdesc) - params = opts.get('params', {}) - params.update(rtdata) - opts['params'] = params - - desc = self.getUrlData(url,**opts) - if not (isinstance(desc, DictObject) or \ - isinstance(desc, dict)): - print('Block2: desc must be a dict object', - desc,type(desc)) - self.dispatch('on_failed',Exception('miss url')) - return - - if addon: - desc = dictExtend(desc,addon) - widgettype = desc.get('widgettype') - if widgettype is None: - print('Block3: desc must be a dict object not None') - return None - return doit(desc) - - @classmethod - def findWidget(self, id:str, from_widget:Widget=None) -> Widget: - """ - -:find up direct, find widget in ancestor - + or empty: find down direct, find widget in descendant - @:find widget by class - $ or empty:find widget by id - .:more than one step. - - """ - def find_widget_by_id(id, from_widget): - if id=='self': - return from_widget - if hasattr(from_widget,'widget_id'): - if from_widget.widget_id == id: - return from_widget - if hasattr(from_widget, id): - w = getattr(from_widget,id) - return w - return None - - def find_widget_by_class(klass, from_widget): - if from_widget.__class__.__name__ == klass: - return from_widget - for k, v in from_widget.__dict__.items(): - if isinstance(v, Widget): - if v.__class__.__name__ == klass: - return v - return None - - def _find_widget(name, from_widget, dir='down', - find_func=find_widget_by_id): - w = find_func(name, from_widget) - if w: - return w - if dir == 'down': - children = [i for i in from_widget.children] - if hasattr(from_widget, 'get_subwidgets'): - children = from_widget.get_subwidgets() - Logger.info('children=%s', str(children)) - for c in children: - ret = _find_widget(name, from_widget=c, dir=dir) - if ret: - return ret - else: - if isinstance(from_widget, WindowBase): - return None - if from_widget.parent: - return _find_widget(name, - from_widget=from_widget.parent, - dir=dir) - Logger.info('Block:_find_widget(), %s, %s %s return None', - name, from_widget.__class__.__name__, dir) - return None - - def find_widget(step, from_widget): - dir = 'down' - if step[0:1] == '-': - dir = 'up' - step = step[1:] - find_func = find_widget_by_id - if step[0:1] == '@': - step = step[1:] - find_func = find_widget_by_class - return _find_widget(step, from_widget=from_widget, - dir=dir, find_func=find_func) - - steps = id.split('.') - w = from_widget - for i, s in enumerate(steps): - if i==0: - app = App.get_running_app() - fid = s - if fid == '/self' or fid == 'root': - w = app.root - if len(steps) == 1: - return from_widget - continue - if fid == 'Window': - w = Window - if len(steps) == 1: - return w - continue - if fid == 'app': - return app - - w = find_widget(s, w) - if not w: - return None - return w - - @classmethod - def getWidgetById(self, id:str, from_widget:Widget=None) -> Widget: - return Blocks.findWidget(id, from_widget=from_widget) - - def on_built(self,v=None): - return - - def on_failed(self,e=None): - return - - def buildKlass(self, desc): - """ - desc = { - "classname":"MyClass", - "base":["Box", "WidgetReady"], - "properties":[ - { - "name":"aaa", - "type":"str", - "default":1 - } - ] - "subwidgets":[ - ], - - """ - codes = """ -{% for b in bases %} -{{b}} = Factory.{{b}} -{% endfor %} - -class {{ classname }}({% for b in bases -%}{{b}}{% endfor %}) -{% for p in properties %} - {{p.name}} {{p.type}}({{p.default}}) -{% endfor %} - def __init__(self, **kw): - super({{classname}}, self).__init__(**kw) - -""" - -Factory.register('Blocks',Blocks) -Factory.register('Video',Video) -Factory.register('OrientationLayout', OrientationLayout) +import os +import sys +import codecs +# import ujson as json +try: + import ujson as json +except: + import json +from traceback import print_exc + +from functools import partial + +from appPublic.dictExt import dictExtend +from appPublic.folderUtils import ProgramPath +from appPublic.dictObject import DictObject +from appPublic.Singleton import SingletonDecorator, GlobalEnv +from appPublic.datamapping import keyMapping +from appPublic.registerfunction import RegisterFunction + +from kivy.logger import Logger +from kivy.config import Config +from kivy.metrics import sp,dp,mm +from kivy.core.window import WindowBase, Window +from kivy.properties import BooleanProperty +from kivy.uix.widget import Widget +from kivy.uix.modalview import ModalView +from kivy.app import App +from kivy.factory import Factory +from kivy.uix.video import Video +from .utils import * +from .newvideo import Video +from .orientationlayout import OrientationLayout +from .threadcall import HttpClient +from .register import * +from .script import Script, set_script_env + +class WidgetNotFoundById(Exception): + def __init__(self, id): + super().__init__() + self.idstr = id + + def __str__(self): + return "Widget not found by id:" + self.idstr + ':' + + def __expr__(self): + return str(self) + +class ClassMethodNotFound(Exception): + def __init__(self,k,m): + super().__init__() + self.kname = k + self.mname = m + + def __str__(self): + s = 'Method(%s) not found in class(%s)' % (self.mname, + str(self.kname.__classname__)) + return s + + def __expr__(self): + return self.__str__() + + +class NotExistsObject(Exception): + def __init__(self,name): + super().__init__() + self.name = name + + def __str__(self): + s = 'not exists widget(%s)' % self.name + return s + + def __expr__(self): + return self.__str__() + +class ArgumentError(Exception): + def __init__(self,argument,desc): + super().__init__() + self.argument = argument + self.desc = desc + + def __str__(self): + s = 'argument(%s) missed:%s' % (self.argument,self.desc) + return s + + def __expr__(self): + return self.__str__() + + +class NotRegistedWidget(Exception): + def __init__(self, name:str): + super().__init__() + self.widget_name = name + + def __str__(self): + s = 'not reigsted widget(%s)' % self.name + return s + + def __expr__(self): + return self.__str__() + +def registerWidget(name:str,widget): + globals()[name] = widget + + +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') + self.env = GlobalEnv() + config = getConfig() + if config.script_root: + self.script = Script(config.script_root) + else: + self.script = Script(config.workdir) + + def set(self, k:str, v): + self.env[k] = v + + def register_widget(self, name:str, widget:Widget): + globals()[name] = widget + + def buildAction(self, widget:Widget, desc): + conform_desc = desc.get('conform') + blocks = Blocks() + if not conform_desc: + return partial(blocks.uniaction, widget, desc) + func =partial(blocks.conform_action,widget, desc) + return func + + def eval(self, s:str, l:dict): + g = {} + forbidens = [ + "os", + "sys", + "codecs", + "json", + ] + + for k,v in globals().copy().items(): + if k not in forbidens: + g[k] = v + + """ + g['__builtins__'] = globals()['__builtins__'] + g['__builtins__']['__import__'] = None + g['__builtins__']['__loader__'] = None + g['__builtins__']['open'] = None + """ + g.update(self.env) + return eval(s,g,l) + + def getUrlData(self, url:str, method:str='GET', + params:dict={}, files:dict={}, + callback=None, + errback=None,**kw): + + if url is None: + if errback: + errback(None,Exception('url is None')) + else: + return None + + if url.startswith('file://'): + return self.script.dispatch(url, **params) + elif url.startswith('http://') or url.startswith('https://'): + try: + hc = HttpClient() + resp=hc(url,method=method,params=params,files=files) + return resp + except Exception as e: + print_exc() + if errback: + return errback(None,e) + return None + else: + config = getConfig() + url = config.uihome + url + return self.getUrlData(url,method=method, + params=params, + files=files, + **kw) + + def strValueExpr(self,s:str,localnamespace:dict={}): + if not s.startswith('py::'): + return s + s = s[4:] + try: + v = self.eval(s,localnamespace) + return v + except Exception as e: + print('Exception .... ',e,'script=',s) + print_exc() + return s + + def arrayValueExpr(self,arr:list,localnamespace:dict={}) -> list: + d = [] + for v in arr: + if type(v) == type(''): + d.append(self.strValueExpr(v,localnamespace)) + continue + if type(v) == type([]): + d.append(self.arrayValueExpr(v,localnamespace)) + continue + if type(v) == type({}): + d.append(self.dictValueExpr(v,localnamespace)) + continue + if type(v) == type(DictObject): + d.append(self.dictValueExpr(v,localnamespace)) + continue + d.append(v) + return d + + def dictValueExpr(self,dic:dict,localnamespace:dict={}) -> dict: + d = {} + for k,v in dic.items(): + if type(v) == type(''): + d[k] = self.strValueExpr(v,localnamespace) + continue + if type(v) == type([]): + d[k] = self.arrayValueExpr(v,localnamespace) + continue + if type(v) == type({}): + d[k] = self.dictValueExpr(v,localnamespace) + continue + if type(v) == type(DictObject): + d[k] = self.dictValueExpr(v,localnamespace) + continue + d[k] = v + return d + def valueExpr(self,obj,localnamespace:dict={}): + if type(obj) == type(''): + return self.strValueExpr(obj,localnamespace) + if type(obj) == type([]): + return self.arrayValueExpr(obj,localnamespace) + if type(obj) == type({}): + return self.dictValueExpr(obj,localnamespace) + if isinstance(obj,DictObject): + return self.dictValueExpr(obj,localnamespace) + return obj + + def w_build(self,desc) -> Widget: + widgetClass = desc.get('widgettype',None) + if not widgetClass: + Logger.info("Block: w_build(), desc invalid", desc) + raise Exception(desc) + + widgetClass = desc['widgettype'] + opts = self.valueExpr(desc.get('options',{}).copy()) + widget = None + try: + klass = Factory.get(widgetClass) + widget = klass(**opts) + except Exception as e: + print('Error:',widgetClass,'contructon error') + print_exc() + raise NotExistsObject(widgetClass) + + if desc.get('id'): + id = desc.get('id') + if id.startswith('app.'): + app = App.get_running_app() + id = id[4:] + setattr(app, id, widget) + if id.startswith('root.'): + app = App.get_running_app() + id = id[5:] + setattr(app.root, id, widget) + + if '.' in id: + Logger.info('widget id(%s) can not contain "."', id) + else: + widget.widget_id = id + + widget.build_desc = desc + self.build_attributes(widget, desc) + self.build_rest(widget, desc) + self.buildBinds(widget, desc) + return widget + + def build_attributes(self, widget:Widget,desc,t=None): + excludes = ['widgettype','options','subwidgets','binds'] + for k,v in [(k,v) for k,v in desc.items() if k not in excludes]: + if isinstance(v,dict) and v.get('widgettype'): + b = Blocks() + v = self.valueExpr(v, localnamespace={'self':widget}) + w = b.widgetBuild(v) + if hasattr(widget,k): + aw = getattr(widget,k) + if isinstance(aw,Layout): + aw.add_widget(w) + continue + setattr(widget,k,w) + continue + setattr(widget,k,self.valueExpr(v,\ + localnamespace={'self':widget})) + + def build_rest(self, widget:Widget,desc,t=None): + self.subwidget_total = len(desc.get('subwidgets',[])) + self.subwidgets = [ None for i in range(self.subwidget_total)] + pos = 0 + for pos,sw in enumerate(desc.get('subwidgets',[])): + b = Blocks() + if isinstance(sw, str): + # w = Blocks.getWidgetById(sw, from_widget=widget) + w = Blocks.findWidget(sw, from_widget=widget) + if w: + widget.add_widget(w) + continue + + kw = self.valueExpr(sw.copy(), + localnamespace={'self':widget}) + w = b.widgetBuild(kw) + if w: + widget.add_widget(w) + + + def buildBinds(self, widget:Widget, desc:dict): + for b in desc.get('binds',[]): + kw = self.valueExpr(b.copy(), \ + localnamespace={'self':widget}) + self.buildBind(widget,kw) + + def buildBind(self, widget:Widget, desc:dict): + wid = desc.get('wid','self') + # w = Blocks.getWidgetById(desc.get('wid','self'),from_widget=widget) + w = Blocks.findWidget(desc.get('wid','self'),from_widget=widget) + if not w: + Logger.info('Block: id(%s) %s',desc.get('wid','self'), + 'not found via Blocks.getWidgetById()') + return + event = desc.get('event') + if event is None: + Logger.info('Block: binds desc miss event, desc=%s',str(desc)) + return + f = self.buildAction(widget,desc) + if f is None: + Logger.info('Block: get a null function,%s',str(desc)) + return + w.bind(**{event:f}) + + def multipleAction(self, widget:Widget, desc, *args): + desc1 = {k:v for k, v in desc.items() if k != 'actions'} + mydesc = desc1.copy() + for a in desc['actions']: + new_desc = mydesc.copy() + new_desc.update(a) + self.uniaction(widget,new_desc, *args) + + def conform_action(self, widget:Widget, desc, *args): + conform_desc = desc.get('conform') + blocks = Blocks() + if not conform_desc: + blocks.uniaction(widget, desc,*args, **kw) + return + w = blocks.widgetBuild({ + "widgettype":"Conform", + "options":conform_desc + }) + w.bind(on_conform=partial(blocks.uniaction, widget, desc)) + w.open() + + def uniaction(self, widget:Widget, desc, *args): + acttype = desc.get('actiontype') + if acttype=='blocks': + return self.blocksAction(widget,desc, *args) + if acttype=='urlwidget': + return self.urlwidgetAction(widget,desc, *args) + if acttype == 'registedfunction': + return self.registedfunctionAction(widget,desc, *args) + if acttype == 'script': + return self.scriptAction(widget, desc, *args) + if acttype == 'method': + return self.methodAction(widget, desc, *args) + if acttype == 'event': + return self.eventAction(widget, desc, *args) + if acttype == 'multiple': + return self.multipleAction(widget, desc, *args) + + alert("actiontype(%s) invalid" % acttype,title='error') + + def eventAction(self, widget:Widget, desc, *args): + target = self.get_target(widget, desc) + event = desc.get('dispatch_event') + if not event: + Logger.info('Block: eventAction():desc(%s) miss dispatch_event', + str(desc)) + return + params = desc.get('params',{}) + d = self.getActionData(widget,desc, *args) + if d: + params.update(d) + try: + target.dispatch(event, params) + except Exception as e: + Logger.info(f'Block: eventAction():dispatch {event} error') + print_exc() + return + + def get_target(self, widget:Widget, desc): + if not desc.get('target'): + return None + # return Blocks.getWidgetById(desc.get('target'),from_widget=widget) + return Blocks.findWidget(desc.get('target'),from_widget=widget) + + def blocksAction(self, widget:Widget, desc, *args): + target = self.get_target(widget, desc) + add_mode = desc.get('mode','replace') + opts = desc.get('options').copy() + d = self.getActionData(widget,desc, *args) + p = opts.get('options',{}).copy() + if d: + p.update(d) + opts['options'] = p + def doit(target:Widget, add_mode:str, o, w:Widget): + if isinstance(w, ModalView): + return + + if target and not w.parent: + if add_mode == 'replace': + target.clear_widgets() + target.add_widget(w) + + def doerr(o,e): + Logger.info('Block: blocksAction(): desc=%s widgetBuild error' + ,str(desc)) + raise e + + b = Blocks() + b.bind(on_built=partial(doit,target,add_mode)) + b.bind(on_failed=doerr) + b.widgetBuild(opts) + + def urlwidgetAction(self, widget:Widget, desc, *args): + target = self.get_target(widget, desc) + add_mode = desc.get('mode','replace') + opts = desc.get('options', {}).copy() + p1 = opts.get('params',{}) + p = {} + if p1: + p.update(p1) + if len(args) >= 1 and isinstance(args[0],dict): + p.update(args[0]) + d = self.getActionData(widget, desc, *args) + if d: + p.update(d) + opts['params'] = p + d = { + 'widgettype' : 'urlwidget', + 'options': opts + } + + def doit(target:Widget, add_mode:str, o, w:Widget): + if isinstance(w, ModalView): + return + + if target and not w.parent: + if add_mode == 'replace': + target.clear_widgets() + target.add_widget(w) + + def doerr(o,e): + Logger.info('Block: urlwidgetAction(): desc=%s widgetBuild error' + ,str(desc)) + + b = Blocks() + b.bind(on_built=partial(doit,target,add_mode)) + b.bind(on_failed=doerr) + b.widgetBuild(d) + + def getActionData(self, widget:Widget, desc, *args): + data = {} + rtdesc = self.build_rtdesc(desc) + if rtdesc: + rt = self.get_rtdata(widget, rtdesc, *args) + if rt: + data.update(rt) + if desc.get('keymapping'): + data = keyMapping(data, desc.get('keymapping')) + Logger.info('getActionData():rtdesc=%s, data=%s', rtdesc, data) + return data + + def registedfunctionAction(self, widget:Widget, desc, *args): + target = self.get_target(widget, desc) + rf = RegisterFunction() + name = desc.get('rfname') + func = rf.get(name) + if func is None: + Logger.info('Block: desc=%s rfname(%s) not found', + str(desc), name) + raise Exception('rfname(%s) not found' % name) + + params = desc.get('params',{}).copy() + d = self.getActionData(widget,desc, *args) + if d: + params.update(d) + func(target, *args, **params) + + def scriptAction(self, widget:Widget, desc, *args): + script = desc.get('script') + if not script: + Logger.info('Block: scriptAction():desc(%s) target not found', + str(desc)) + return + target = self.get_target(widget, desc) + d = self.getActionData(widget,desc, *args) + ns = { + "self":target, + "args":args, + "kwargs":d + } + if d: + ns.update(d) + try: + self.eval(script, ns) + except Exception as e: + print_exc() + print(e) + + def build_rtdesc(self, desc): + rtdesc = desc.get('rtdata') + if not rtdesc: + if desc.get('datawidget'): + rtdesc = {} + rtdesc['target'] = desc['datawidget'] + if desc.get('datascript'): + rtdesc['script'] = desc.get('datacript') + else: + rtdesc['method'] = desc.get('datamethod', 'getValue') + rtdesc['kwargs'] = desc.get('datakwargs', {}) + return rtdesc + + def get_rtdata(self, widget:Widget, desc, *args): + """ + desc descript follow attributes + { + "target", a target name from widget + "method", a method name, default is 'getValue', + "script", script to handle the data + "kwargs":" a dict arguments, default is {} + } + """ + + if desc is None or desc == {}: + print('get_rtdata():desc is None') + return {} + kwargs = desc.get('kwargs', {}) + if not isinstance(kwargs, dict): + kwargs = {} + w = None + if len(args) > 0: + w = args[0] + target = desc.get('target') + if target: + # w = Blocks.getWidgetById(target, from_widget=widget) + w = Blocks.findWidget(target, from_widget=widget) + if w is None: + print('get_rtdata():w is None', desc) + return {} + script = desc.get('script') + if script: + ns = { + "self":w, + "args":args, + "kwargs":kwargs + } + d = self.eval(script, ns) + return d + + method = desc.get('method', 'getValue') + if not hasattr(w, method): + print('get_rtdata():method is None', desc, type(w)) + return {} + f = getattr(w, method) + try: + r = f(**kwargs) + if isinstance(r, dict): + return r + if isinstance(r, DictObject): + return r.to_dict() + print('get_rtdata():method return is not dict', desc, 'ret=', r, type(r)) + return {} + except: + print_exc() + return {} + + def methodAction(self, widget:Widget, desc, *args): + method = desc.get('method') + target = self.get_target(widget, desc) + if target is None: + Logger.info('Block: methodAction():desc(%s) target not found', + str(desc)) + return + if method is None: + Logger.info('Block: methodAction():desc(%s) method not found', + str(desc)) + return + if hasattr(target,method): + f = getattr(target, method) + kwargs = desc.get('options',{}).copy() + d = self.getActionData(widget,desc, *args) + if d: + kwargs.update(d) + f(*args, **kwargs) + else: + alert('%s method not found' % method) + + def widgetBuild(self, desc): + """ + desc format: + { + widgettype:, + id:widget id, + options:{} + subwidgets:[ + ] + binds:[ + ] + } + """ + def doit(desc): + if isinstance(desc,DictObject): + desc = desc.to_dict() + if not isinstance(desc,dict): + Logger.info('Block: desc(%s) must be a dict object(%s)', + desc,type(desc)) + return None + + # desc = self.valueExpr(desc) + try: + widget = self.w_build(desc) + self.dispatch('on_built',widget) + if hasattr(widget,'ready'): + widget.ready = True + return widget + except Exception as e: + print_exc() + self.dispatch('on_failed',e) + return None + + if isinstance(desc,DictObject): + desc = desc.to_dict() + if not isinstance(desc, dict): + print('widgetBuild1: desc must be a dict object', + desc,type(desc)) + self.dispatch('on_failed',Exception('miss url')) + return + + widgettype = desc.get('widgettype') + while widgettype == "urlwidget": + opts = desc.get('options',{}).copy() + extend = desc.get('extend') + addon = None + if desc.get('extend'): + addon = desc.get('extend').copy() + url = opts.get('url') + if url is None: + self.dispatch('on_failed',Exception('miss url')) + return + + if opts.get('url'): + del opts['url'] + rtdesc = self.build_rtdesc(opts) + if rtdesc: + rtdata = self.get_rtdata(None, rtdesc) + params = opts.get('params', {}) + params.update(rtdata) + opts['params'] = params + + desc = self.getUrlData(url,**opts) + if not (isinstance(desc, DictObject) or \ + isinstance(desc, dict)): + print('Block2: desc must be a dict object', + desc,type(desc)) + self.dispatch('on_failed',Exception('miss url')) + return + + if addon: + desc = dictExtend(desc,addon) + widgettype = desc.get('widgettype') + if widgettype is None: + print('Block3: desc must be a dict object not None') + return None + return doit(desc) + + @classmethod + def findWidget(self, id:str, from_widget:Widget=None) -> Widget: + """ + -:find up direct, find widget in ancestor + + or empty: find down direct, find widget in descendant + @:find widget by class + $ or empty:find widget by id + .:more than one step. + + """ + def find_widget_by_id(id, from_widget): + if id=='self': + return from_widget + if hasattr(from_widget,'widget_id'): + if from_widget.widget_id == id: + return from_widget + if hasattr(from_widget, id): + w = getattr(from_widget,id) + return w + return None + + def find_widget_by_class(klass, from_widget): + if from_widget.__class__.__name__ == klass: + return from_widget + for k, v in from_widget.__dict__.items(): + if isinstance(v, Widget): + if v.__class__.__name__ == klass: + return v + return None + + def _find_widget(name, from_widget, dir='down', + find_func=find_widget_by_id): + w = find_func(name, from_widget) + if w: + return w + if dir == 'down': + children = [i for i in from_widget.children] + if hasattr(from_widget, 'get_subwidgets'): + children = from_widget.get_subwidgets() + Logger.info('children=%s', str(children)) + for c in children: + ret = _find_widget(name, from_widget=c, dir=dir) + if ret: + return ret + else: + if isinstance(from_widget, WindowBase): + return None + if from_widget.parent: + return _find_widget(name, + from_widget=from_widget.parent, + dir=dir) + Logger.info('Block:_find_widget(), %s, %s %s return None', + name, from_widget.__class__.__name__, dir) + return None + + def find_widget(step, from_widget): + dir = 'down' + if step[0:1] == '-': + dir = 'up' + step = step[1:] + find_func = find_widget_by_id + if step[0:1] == '@': + step = step[1:] + find_func = find_widget_by_class + return _find_widget(step, from_widget=from_widget, + dir=dir, find_func=find_func) + + steps = id.split('.') + w = from_widget + for i, s in enumerate(steps): + if i==0: + app = App.get_running_app() + fid = s + if fid == '/self' or fid == 'root': + w = app.root + if len(steps) == 1: + return from_widget + continue + if fid == 'Window': + w = Window + if len(steps) == 1: + return w + continue + if fid == 'app': + return app + + w = find_widget(s, w) + if not w: + return None + return w + + @classmethod + def getWidgetById(self, id:str, from_widget:Widget=None) -> Widget: + return Blocks.findWidget(id, from_widget=from_widget) + + def on_built(self,v=None): + return + + def on_failed(self,e=None): + return + + def buildKlass(self, desc): + """ + desc = { + "classname":"MyClass", + "base":["Box", "WidgetReady"], + "properties":[ + { + "name":"aaa", + "type":"str", + "default":1 + } + ] + "subwidgets":[ + ], + + """ + codes = """ +{% for b in bases %} +{{b}} = Factory.{{b}} +{% endfor %} + +class {{ classname }}({% for b in bases -%}{{b}}{% endfor %}) +{% for p in properties %} + {{p.name}} {{p.type}}({{p.default}}) +{% endfor %} + def __init__(self, **kw): + super({{classname}}, self).__init__(**kw) + +""" + +Factory.register('Blocks',Blocks) +Factory.register('Video',Video) +Factory.register('OrientationLayout', OrientationLayout) diff --git a/kivyblocks/dataloader.py b/kivyblocks/dataloader.py index 9171d46..8197fb8 100644 --- a/kivyblocks/dataloader.py +++ b/kivyblocks/dataloader.py @@ -1,182 +1,193 @@ - -from kivy.event import EventDispatcher -from kivy.app import App -from kivy.factory import Factory -from .threadcall import HttpClient -from .utils import absurl -from appPublic.registerfunction import RegisterFunction - -class DataGraber(EventDispatcher): - """ - Graber format - { - "widgettype":"DataGraber", - "options":{ - "dataurl":"first", - "datarfname":"second", - "target":"third", - "params": - "method": - "pagging":"default false" - } - } - if dataurl present, the DataGraber using this dataurl to get and - return data - else if datarfname present, it find a registered function named - by 'datarfname' to return data - else if datatarget present, it find the widget and uses target - method(default is 'getValue') to return data - else it return None - """ - def __init__(self, **kw): - super().__init__() - self.options = kw - self.register_event_type('on_success') - self.register_event_type('on_error') - - def load(self, *args, **kw): - ret = None - while True: - try: - dataurl = self.options.get('dataurl') - if dataurl: - ret = self.loadUrlData(*args, **kw) - break - - rfname = self.options.get('datarfname') - if rfname: - ret = self.loadRFData(*args, **kw) - break - target = self.options.get('datatarget') - if target: - ret = self.loadTargetData(*args, **kw) - break - except Exception as e: - self.dispatch('on_error', e) - return - if ret: - self.dispatch('on_success',ret) - else: - e = Exception('Not method to do load') - self.dispatch('on_error', e) - - def loadUrlData(self, *args, **kw): - dataurl = self.options.get('dataurl') - hc = HttpClient() - params = self.options.get('params',{}).copy() - params.update(kw) - method = self.options.get('method','GET') - d = hc(dataurl, params=params,method=method) - return d - - def loadRFData(self, *args, **kw): - rfname = self.options.get('datarfname') - rf = RegisterFunction() - f = rf.get(rfname) - if not f: - return None - params = self.options.get('params',{}).copy() - params.update(kw) - try: - d = f(**params) - return d - except Exception as e: - Logger.info('blocks : Exception:%s',e) - print_exc() - return None - - def loadTargetData(self, *args, **kw): - target = self.options.get('datatarget') - w = Factory.Blocks.getWidgetById(target) - if not w: - return None - params = self.options.get('params',{}).copy() - params.update(kw) - method = params.get('method', 'getValue') - if not has(w, method): - return None - try: - f = getattr(w, method) - d = f() - return d - except Exception as e: - Logger.info('blocks : Exception %s', e) - print_exc() - return None - - def on_success(self,d): - pass - - def on_error(self,e): - pass - -class DataLoader(EventDispatcher): - def __init__(self,data_user): - self.data_user = data_user - EventDispatcher.__init__(self) - self.register_event_type('on_success') - self.register_event_type('on_error') - - def on_success(self,d): - pass - - def on_error(self,e): - pass - - def load(self): - pass - -class HttpDataLoader(DataLoader): - def load(self, *args, **kw): - app = App.get_running_app() - url = app.realurl(self.data_user.url) - method = self.data_user.method - params = self.data_user.params.copy() - params.update({ - "page":self.data_user.curpage, - "rows":self.data_user.page_rows - }) - hc = HttpClient() - try: - r = hc(url, method=method, params=params) - self.dispatch('on_success', r) - return r - except Exception as e: - self.dispatch('on_error', e) - - -class ListDataLoader(DataLoader): - def load(self, *args, **kw): - p = self.data_user.curpage - r = self.data_user.page_rows - try: - s = self.data_user.data[(p-1)*r:p*r] - d = { - "total":len(self.data_user.data), - "rows":s - } - self.dispatch('on_success', d) - return d - except Exception as e: - self.dispatch('on_error', e) - -class RegisterFunctionDataLoader(DataLoader): - def load(self, *args, **kw): - rf = RegisterFunction() - try: - rfname = self.data_user.rfname - func = rf.get(rfname) - if func is None: - raise Exception('%s is not a registerfunction' % rfname) - params = {k:v for k,v in self.user_data.params.items()} - params.update({ - "page":self.data_user.curpage, - "rows":self.data_user.page_rows - }) - s = func(**params) - self.dispatch('on_success', s) - return s - except Exception as e: - self.dispatch('on_error', e) - - + +from kivy.event import EventDispatcher +from kivy.app import App +from kivy.factory import Factory +from .threadcall import HttpClient +from .utils import absurl +from appPublic.registerfunction import RegisterFunction + +def getUrlData(url, method='GET', params={}): + blocks = Factory.Blocks() + d = blocks.getUrlData(url, method=method, + params=params) + return d + +class DataGraber(EventDispatcher): + """ + Graber format + { + "widgettype":"DataGraber", + "options":{ + "dataurl":"first", + "datarfname":"second", + "target":"third", + "params": + "method": + "paging":"default false" + } + } + if dataurl present, the DataGraber using this dataurl to get and + return data + else if datarfname present, it find a registered function named + by 'datarfname' to return data + else if datatarget present, it find the widget and uses target + method(default is 'getValue') to return data + else it return None + """ + def __init__(self, **kw): + super().__init__() + self.options = kw + self.register_event_type('on_success') + self.register_event_type('on_error') + + def load(self, *args, **kw): + ret = None + if self.options['paging']: + if kw.get('rows') is None: + kw['rows'] = 60 + if kw.get('page') is None: + kw['page'] = 1 + while True: + try: + dataurl = self.options.get('dataurl') + if dataurl: + ret = self.loadUrlData(*args, **kw) + break + + rfname = self.options.get('datarfname') + if rfname: + ret = self.loadRFData(*args, **kw) + break + target = self.options.get('datatarget') + if target: + ret = self.loadTargetData(*args, **kw) + break + except Exception as e: + self.dispatch('on_error', e) + return + if ret: + self.dispatch('on_success',ret) + else: + e = Exception('Not method to do load') + self.dispatch('on_error', e) + + def loadUrlData(self, *args, **kw): + dataurl = self.options.get('dataurl') + params = self.options.get('params',{}).copy() + params.update(kw) + method = self.options.get('method','GET') + d = getUrlData(dataurl, method=method, + params=params) + return d + + def loadRFData(self, *args, **kw): + rfname = self.options.get('datarfname') + rf = RegisterFunction() + f = rf.get(rfname) + if not f: + return None + params = self.options.get('params',{}).copy() + params.update(kw) + try: + d = f(**params) + return d + except Exception as e: + Logger.info('blocks : Exception:%s',e) + print_exc() + return None + + def loadTargetData(self, *args, **kw): + target = self.options.get('datatarget') + w = Factory.Blocks.getWidgetById(target) + if not w: + return None + params = self.options.get('params',{}).copy() + params.update(kw) + method = params.get('method', 'getValue') + if not has(w, method): + return None + try: + f = getattr(w, method) + d = f() + return d + except Exception as e: + Logger.info('blocks : Exception %s', e) + print_exc() + return None + + def on_success(self,d): + pass + + def on_error(self,e): + pass + +class DataLoader(EventDispatcher): + def __init__(self,data_user): + self.data_user = data_user + EventDispatcher.__init__(self) + self.register_event_type('on_success') + self.register_event_type('on_error') + + def on_success(self,d): + pass + + def on_error(self,e): + pass + + def load(self): + pass + +class UrlDataLoader(DataLoader): + def load(self, *args, **kw): + app = App.get_running_app() + url = app.realurl(self.data_user.url) + method = self.data_user.method + params = self.data_user.params.copy() + params.update({ + "page":self.data_user.curpage, + "rows":self.data_user.page_rows + }) + + try: + r = getUrlData(url, method=method, params=params) + self.dispatch('on_success', r) + return r + except Exception as e: + self.dispatch('on_error', e) + + +class ListDataLoader(DataLoader): + def load(self, *args, **kw): + p = self.data_user.curpage + r = self.data_user.page_rows + try: + s = self.data_user.data[(p-1)*r:p*r] + d = { + "total":len(self.data_user.data), + "rows":s + } + self.dispatch('on_success', d) + return d + except Exception as e: + self.dispatch('on_error', e) + +class RegisterFunctionDataLoader(DataLoader): + def load(self, *args, **kw): + rf = RegisterFunction() + try: + rfname = self.data_user.rfname + func = rf.get(rfname) + if func is None: + raise Exception('%s is not a registerfunction' % rfname) + params = {k:v for k,v in self.user_data.params.items()} + params.update({ + "page":self.data_user.curpage, + "rows":self.data_user.page_rows + }) + s = func(**params) + self.dispatch('on_success', s) + return s + except Exception as e: + self.dispatch('on_error', e) + + diff --git a/kivyblocks/paging.py b/kivyblocks/paging.py index 453c207..49af829 100644 --- a/kivyblocks/paging.py +++ b/kivyblocks/paging.py @@ -1,324 +1,324 @@ -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 -from kivy.app import App -from functools import partial -from appPublic.dictObject import DictObject -from appPublic.jsonConfig import getConfig -from .baseWidget import Text, HTTPDataHandler -from .utils import CSize, absurl, alert -from .form import StrSearchForm -from .dataloader import HttpDataLoader -from .dataloader import ListDataLoader -from .dataloader import RegisterFunctionDataLoader - -class PagingButton(Button): - def __init__(self, **kw): - super().__init__(**kw) - self.size_hint = (None,None) - self.size = CSize(2,1.8) - self.font_size = CSize(1) - -""" -{ - dataurl - params - method - 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 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') - self.rfname = options.get('rfname') - self.data = options.get('data') - self.page_rows = options.get('page_rows',60) - self.total_cnt = 0 - self.total_page = 0 - self.curpage = 0 - self.dir = 'down' - self.register_event_type('on_newbegin') - self.register_event_type('on_pageloaded') - self.newbegin = True - if self.url: - self.loader = HttpDataLoader(self) - elif self.rfname: - self.loader = RegisterFunctionDataLoader(self) - elif self.data: - self.loader = ListDataLoader(self) - else: - raise Exception('need a url or rfname or data') - self.loader.bind(on_success=self.show_page) - self.loader.bind(on_error=self.onerror) - - def on_newbegin(self): - pass - - def on_pageloaded(self,d): - """ - d={ - page: - data:[] - dir:'up' or 'down' - } - """ - - def do_search(self,o,params): - self.newbegin = True - self.total_cnt = 0 - self.total_page = 0 - self.curpage = 0 - self.dir = 'down' - self.dispatch('on_newbegin') - self.params.update(params) - self.loadPage(1) - - def calculateTotalPage(self): - self.total_page = math.floor(self.total_cnt / self.page_rows) - if self.total_cnt % self.page_rows != 0: - self.total_page += 1 - - def setPageRows(self,row_cnt): - if row_cnt == 0: - return - self.page_rows = row_cnt - if self.total_cnt != 0: - self.calculateTotalPage() - - 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) - self.loading = False - - def onerror(self,o,e): - traceback.print_exc() - alert(str(e),title='alert') - self.loading = False - - def loadNextPage(self): - if self.loading: - print('is loading, return') - return - - if self.total_page > 0 and self.curpage >=self.total_page: - return - p = self.curpage + 1 - self.loadPage(p) - - def loadPreviousPage(self): - if self.loading: - print('is loading, return') - return - - if self.curpage <= 1: - return - p = self.curpage - 1 - self.loadPage(p) - - def loadPage(self,p): - if p == self.curpage: - return - - if self.loading: - print('is loading, return') - return - - self.loading = True - print(f'loading {p} page, {self.curpage}') - if self.curpage > p: - self.dir = 'up' - else: - self.dir = 'down' - self.curpage = p - self.loader.load() - -""" -{ - adder, - remover - target, - locater, - dataurl, - params, - method, - filter -} -events: -'on_deletepage': erase - -""" -class RelatedLoader(PageLoader): - def __init__(self, **options): - super().__init__(**options) - 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 do_search(self,o,params): - self.objectPages = {} - self.totalObj = 0 - self.MaxbufferPage = 3 - self.locater = 1/self.MaxbufferPage - super().do_search(o, params) - - def getLocater(self): - if self.newbegin: - self.newbegin = False - return 1 - - x = 1 / self.MaxbufferPage - if self.dir != 'down': - x = 1 - x - return x - - def on_deletepage(self,d): - pass - - def setPageRows(self,row_cnt): - if self.total_cnt != 0: - self.calculateTotalPage() - - def doBufferMaintain(self): - siz = len(self.objectPages.keys()) - if siz >= self.MaxbufferPage: - if self.dir == 'up': - p = max(self.objectPages.keys()) - else: - p = min(self.objectPages.keys()) - self.deleteBuffer(p) - - def deleteBuffer(self,page): - d = self.objectPages[page] - self.dispatch('on_deletepage',d) - self.totalObj -= len(self.objectPages[page]) - del self.objectPages[page] - - def bufferObjects(self,page,objects): - self.objectPages[page] = objects - - - def show_page(self,o,data): - if self.objectPages.get(self.curpage): - self.deleteBuffer(self.curpage) - else: - self.doBufferMaintain() - self.totalObj += len(data['rows']) - super().show_page(o,data) - - def loadPreviousPage(self): - pages = self.objectPages.keys() - if len(pages) < 1: - return - - page = min(pages) - if page <= 1: - return - - page -= 1 - self.loadPage(page) - - def loadNextPage(self): - pages = self.objectPages.keys() - if len(pages) == 0: - return - page = max(pages) - if page>=self.total_page: - return - page += 1 - self.loadPage(page) - -""" -{ - adder, - clearer - target, - dataurl - params, - method -} -""" -class Paging(PageLoader): - def __init__(self,**options): - PageLoader.__init__(self,**options) - self.target = options.get('target') - self.init() - - def init(self): - kwargs = {} - kwargs['size_hint_y'] = None - kwargs['height'] = CSize(2) - kwargs['orientation'] = 'horizontal' - kwargs['spacing'] = CSize(1) - self.widget = BoxLayout(**kwargs) - self.b_f = PagingButton(text="|<") - self.b_p = PagingButton(text="<") - self.b_n = PagingButton(text=">") - self.b_l = PagingButton(text=">|") - self.b_f.bind(on_press=self.loadFirstPage) - self.b_p.bind(on_press=self.loadPreviousPage) - self.b_n.bind(on_press=self.loadNextPage) - self.b_l.bind(on_press=self.loadLastPage) - self.widget.add_widget(self.b_f) - self.widget.add_widget(self.b_p) - self.widget.add_widget(self.b_n) - self.widget.add_widget(self.b_l) - if self.filter: - self.widget.add_widget(self.filter) - - def loadFirstPage(self,o=None): - if self.curpage == 1: - return - self.loadPage(1) - - def loadPreviousPage(self,o=None): - if self.curpage < 2: - return - self.loadPage(self.curpage-1) - - def loadNextPage(self,o=None): - if self.curpage >= self.total_page: - return - self.loadPage(self.curpage+1) - - def loadLastPage(self,o=None): - if self.curpage >= self.total_page: - return - self.loadPage(self.total_page) - +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 +from kivy.app import App +from functools import partial +from appPublic.dictObject import DictObject +from appPublic.jsonConfig import getConfig +from .baseWidget import Text, HTTPDataHandler +from .utils import CSize, absurl, alert +from .form import StrSearchForm +from .dataloader import UrlDataLoader +from .dataloader import ListDataLoader +from .dataloader import RegisterFunctionDataLoader + +class PagingButton(Button): + def __init__(self, **kw): + super().__init__(**kw) + self.size_hint = (None,None) + self.size = CSize(2,1.8) + self.font_size = CSize(1) + +""" +{ + dataurl + params + method + 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 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') + self.rfname = options.get('rfname') + self.data = options.get('data') + self.page_rows = options.get('page_rows',60) + self.total_cnt = 0 + self.total_page = 0 + self.curpage = 0 + self.dir = 'down' + self.register_event_type('on_newbegin') + self.register_event_type('on_pageloaded') + self.newbegin = True + if self.url: + self.loader = UrlDataLoader(self) + elif self.rfname: + self.loader = RegisterFunctionDataLoader(self) + elif self.data: + self.loader = ListDataLoader(self) + else: + raise Exception('need a url or rfname or data') + self.loader.bind(on_success=self.show_page) + self.loader.bind(on_error=self.onerror) + + def on_newbegin(self): + pass + + def on_pageloaded(self,d): + """ + d={ + page: + data:[] + dir:'up' or 'down' + } + """ + + def do_search(self,o,params): + self.newbegin = True + self.total_cnt = 0 + self.total_page = 0 + self.curpage = 0 + self.dir = 'down' + self.dispatch('on_newbegin') + self.params.update(params) + self.loadPage(1) + + def calculateTotalPage(self): + self.total_page = math.floor(self.total_cnt / self.page_rows) + if self.total_cnt % self.page_rows != 0: + self.total_page += 1 + + def setPageRows(self,row_cnt): + if row_cnt == 0: + return + self.page_rows = row_cnt + if self.total_cnt != 0: + self.calculateTotalPage() + + 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) + self.loading = False + + def onerror(self,o,e): + traceback.print_exc() + alert(str(e),title='alert') + self.loading = False + + def loadNextPage(self): + if self.loading: + print('is loading, return') + return + + if self.total_page > 0 and self.curpage >=self.total_page: + return + p = self.curpage + 1 + self.loadPage(p) + + def loadPreviousPage(self): + if self.loading: + print('is loading, return') + return + + if self.curpage <= 1: + return + p = self.curpage - 1 + self.loadPage(p) + + def loadPage(self,p): + if p == self.curpage: + return + + if self.loading: + print('is loading, return') + return + + self.loading = True + print(f'loading {p} page, {self.curpage}') + if self.curpage > p: + self.dir = 'up' + else: + self.dir = 'down' + self.curpage = p + self.loader.load() + +""" +{ + adder, + remover + target, + locater, + dataurl, + params, + method, + filter +} +events: +'on_deletepage': erase + +""" +class RelatedLoader(PageLoader): + def __init__(self, **options): + super().__init__(**options) + 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 do_search(self,o,params): + self.objectPages = {} + self.totalObj = 0 + self.MaxbufferPage = 3 + self.locater = 1/self.MaxbufferPage + super().do_search(o, params) + + def getLocater(self): + if self.newbegin: + self.newbegin = False + return 1 + + x = 1 / self.MaxbufferPage + if self.dir != 'down': + x = 1 - x + return x + + def on_deletepage(self,d): + pass + + def setPageRows(self,row_cnt): + if self.total_cnt != 0: + self.calculateTotalPage() + + def doBufferMaintain(self): + siz = len(self.objectPages.keys()) + if siz >= self.MaxbufferPage: + if self.dir == 'up': + p = max(self.objectPages.keys()) + else: + p = min(self.objectPages.keys()) + self.deleteBuffer(p) + + def deleteBuffer(self,page): + d = self.objectPages[page] + self.dispatch('on_deletepage',d) + self.totalObj -= len(self.objectPages[page]) + del self.objectPages[page] + + def bufferObjects(self,page,objects): + self.objectPages[page] = objects + + + def show_page(self,o,data): + if self.objectPages.get(self.curpage): + self.deleteBuffer(self.curpage) + else: + self.doBufferMaintain() + self.totalObj += len(data['rows']) + super().show_page(o,data) + + def loadPreviousPage(self): + pages = self.objectPages.keys() + if len(pages) < 1: + return + + page = min(pages) + if page <= 1: + return + + page -= 1 + self.loadPage(page) + + def loadNextPage(self): + pages = self.objectPages.keys() + if len(pages) == 0: + return + page = max(pages) + if page>=self.total_page: + return + page += 1 + self.loadPage(page) + +""" +{ + adder, + clearer + target, + dataurl + params, + method +} +""" +class Paging(PageLoader): + def __init__(self,**options): + PageLoader.__init__(self,**options) + self.target = options.get('target') + self.init() + + def init(self): + kwargs = {} + kwargs['size_hint_y'] = None + kwargs['height'] = CSize(2) + kwargs['orientation'] = 'horizontal' + kwargs['spacing'] = CSize(1) + self.widget = BoxLayout(**kwargs) + self.b_f = PagingButton(text="|<") + self.b_p = PagingButton(text="<") + self.b_n = PagingButton(text=">") + self.b_l = PagingButton(text=">|") + self.b_f.bind(on_press=self.loadFirstPage) + self.b_p.bind(on_press=self.loadPreviousPage) + self.b_n.bind(on_press=self.loadNextPage) + self.b_l.bind(on_press=self.loadLastPage) + self.widget.add_widget(self.b_f) + self.widget.add_widget(self.b_p) + self.widget.add_widget(self.b_n) + self.widget.add_widget(self.b_l) + if self.filter: + self.widget.add_widget(self.filter) + + def loadFirstPage(self,o=None): + if self.curpage == 1: + return + self.loadPage(1) + + def loadPreviousPage(self,o=None): + if self.curpage < 2: + return + self.loadPage(self.curpage-1) + + def loadNextPage(self,o=None): + if self.curpage >= self.total_page: + return + self.loadPage(self.curpage+1) + + def loadLastPage(self,o=None): + if self.curpage >= self.total_page: + return + self.loadPage(self.total_page) +