From e11a39843994dbb1e2f255013bee5bf332843f78 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 24 Apr 2020 18:35:30 +0800 Subject: [PATCH] bugfix --- kivyblocks/baseWidget.py | 53 ++++++---- kivyblocks/bgcolorbehavior.py | 68 ++++++++++++- kivyblocks/blocks.py | 168 ++++++++++++++++++++----------- kivyblocks/boxViewer.py | 67 ++++++++---- kivyblocks/form.py | 45 +++++---- kivyblocks/i18n.py | 2 + kivyblocks/kivysize.py | 11 +- kivyblocks/login.py | 26 ++--- kivyblocks/newwidget.py | 75 ++++++++++++++ kivyblocks/paging.py | 164 +++++++++++++++--------------- kivyblocks/register.py | 41 ++++++++ kivyblocks/tab.py | 35 +++++++ kivyblocks/threadcall.py | 26 +++-- kivyblocks/toolbar.py | 37 ++++--- kivyblocks/tree.py | 29 ++---- kivyblocks/utils.py | 21 ++++ kivyblocks/widgetExt/inputext.py | 74 ++++++++++---- 17 files changed, 645 insertions(+), 297 deletions(-) create mode 100644 kivyblocks/newwidget.py create mode 100644 kivyblocks/register.py create mode 100644 kivyblocks/tab.py diff --git a/kivyblocks/baseWidget.py b/kivyblocks/baseWidget.py index c918e35..9028e36 100755 --- a/kivyblocks/baseWidget.py +++ b/kivyblocks/baseWidget.py @@ -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 diff --git a/kivyblocks/bgcolorbehavior.py b/kivyblocks/bgcolorbehavior.py index 8916940..9c2df1e 100644 --- a/kivyblocks/bgcolorbehavior.py +++ b/kivyblocks/bgcolorbehavior.py @@ -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() diff --git a/kivyblocks/blocks.py b/kivyblocks/blocks.py index 9d7705c..38c8214 100755 --- a/kivyblocks/blocks.py +++ b/kivyblocks/blocks.py @@ -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) - for b in desc.get('binds',[]): - self.buildBind(widget,b) - if hasattr(widget, 'built'): - widget.built() + 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) 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 - if add_mode == 'replace': - target.clear_widgets() - target.add_widget(x) + def doit(target,add_mode,o,w): + if add_mode == 'replace': + target.clear_widgets() + 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 - if add_mode == 'replace': - target.clear_widgets() - target.add_widget(x) + + def doit(target,add_mode,o,w): + if add_mode == 'replace': + target.clear_widgets() + 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: + url = absurl(opts.get('url'),parenturl) + except Exception as e: + self.dispatch('on_failed',e) - try: - widget = self.__build(desc,ancestor=ancestor) - return widget - except: - print('widgetBuild() Error',desc) - print_exc() - alert(str(desc)) - return None + 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) diff --git a/kivyblocks/boxViewer.py b/kivyblocks/boxViewer.py index 2d5fb1a..558bb07 100644 --- a/kivyblocks/boxViewer.py +++ b/kivyblocks/boxViewer.py @@ -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 = LClass(**lopts['options']) + 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) + 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: diff --git a/kivyblocks/form.py b/kivyblocks/form.py index 7f023e4..1b6da28 100644 --- a/kivyblocks/form.py +++ b/kivyblocks/form.py @@ -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') diff --git a/kivyblocks/i18n.py b/kivyblocks/i18n.py index 73bab39..fd308a8 100644 --- a/kivyblocks/i18n.py +++ b/kivyblocks/i18n.py @@ -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): diff --git a/kivyblocks/kivysize.py b/kivyblocks/kivysize.py index 5f4df69..077f48e 100644 --- a/kivyblocks/kivysize.py +++ b/kivyblocks/kivysize.py @@ -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 diff --git a/kivyblocks/login.py b/kivyblocks/login.py index 7c7f1ab..ef95238 100644 --- a/kivyblocks/login.py +++ b/kivyblocks/login.py @@ -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 .....') diff --git a/kivyblocks/newwidget.py b/kivyblocks/newwidget.py new file mode 100644 index 0000000..778febf --- /dev/null +++ b/kivyblocks/newwidget.py @@ -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) diff --git a/kivyblocks/paging.py b/kivyblocks/paging.py index c08a89a..1d8627f 100644 --- a/kivyblocks/paging.py +++ b/kivyblocks/paging.py @@ -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,22 +100,20 @@ 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() alert(str(e),title='alert') @@ -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) """ { diff --git a/kivyblocks/register.py b/kivyblocks/register.py new file mode 100644 index 0000000..e63e77d --- /dev/null +++ b/kivyblocks/register.py @@ -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) diff --git a/kivyblocks/tab.py b/kivyblocks/tab.py new file mode 100644 index 0000000..472da07 --- /dev/null +++ b/kivyblocks/tab.py @@ -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 diff --git a/kivyblocks/threadcall.py b/kivyblocks/threadcall.py index 3151966..ad67661 100644 --- a/kivyblocks/threadcall.py +++ b/kivyblocks/threadcall.py @@ -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) diff --git a/kivyblocks/toolbar.py b/kivyblocks/toolbar.py index 655e75f..200acbf 100644 --- a/kivyblocks/toolbar.py +++ b/kivyblocks/toolbar.py @@ -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) diff --git a/kivyblocks/tree.py b/kivyblocks/tree.py index 7177f4e..0ba621c 100644 --- a/kivyblocks/tree.py +++ b/kivyblocks/tree.py @@ -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 __init__(self,color_level=0,**options): + PressableLabel.__init__(self,color_level=color_level,**options) - def selected(self): - pass - - def unselected(self): - pass - 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): diff --git a/kivyblocks/utils.py b/kivyblocks/utils.py index 9851718..3f87994 100644 --- a/kivyblocks/utils.py +++ b/kivyblocks/utils.py @@ -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::'): diff --git a/kivyblocks/widgetExt/inputext.py b/kivyblocks/widgetExt/inputext.py index a0d9f99..2e0e711 100755 --- a/kivyblocks/widgetExt/inputext.py +++ b/kivyblocks/widgetExt/inputext.py @@ -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 = {}