From 9e2b5db1a634b28fa8ea63cee917c96e4b7ea3ef Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 26 Oct 2023 17:55:34 +0800 Subject: [PATCH] bugfix --- kivyblocks/baseWidget.py | 21 +++--- kivyblocks/blocks.py | 13 +++- kivyblocks/blocksapp.py | 23 +++++- kivyblocks/boxViewer.py | 2 +- kivyblocks/clickable.py | 41 +++++++++++ kivyblocks/container.py | 138 +++++++++++++++++++++++++++-------- kivyblocks/defaultimage.py | 1 + kivyblocks/eventhandler.py | 4 +- kivyblocks/i18n.py | 18 +++-- kivyblocks/imgs/.DS_Store | Bin 12292 -> 12292 bytes kivyblocks/imgs/add.png | Bin 0 -> 10592 bytes kivyblocks/imgs/delete.png | Bin 13894 -> 10410 bytes kivyblocks/imgs/deletex.png | Bin 0 -> 13894 bytes kivyblocks/imgs/lensid.png | Bin 0 -> 17755 bytes kivyblocks/pagepanel.py | 2 + kivyblocks/paging.py | 132 ++++++--------------------------- kivyblocks/register.py | 2 + kivyblocks/scrollpanel.py | 4 +- kivyblocks/tab.py | 50 +++++++++---- kivyblocks/toolbar.py | 49 +++++++++---- kivyblocks/tree.py | 34 +++++++-- kivyblocks/utils.py | 7 +- kivyblocks/widgetExt/tree.py | 24 ------ 23 files changed, 332 insertions(+), 233 deletions(-) create mode 100644 kivyblocks/imgs/add.png mode change 100755 => 100644 kivyblocks/imgs/delete.png create mode 100755 kivyblocks/imgs/deletex.png create mode 100755 kivyblocks/imgs/lensid.png delete mode 100755 kivyblocks/widgetExt/tree.py diff --git a/kivyblocks/baseWidget.py b/kivyblocks/baseWidget.py index 210801a..9a4fece 100755 --- a/kivyblocks/baseWidget.py +++ b/kivyblocks/baseWidget.py @@ -89,16 +89,6 @@ font_names = { 'title1':resource_find('Alimama_ShuHeiTi_Bold.ttf') } -font_sizes = { - 'text':CSize(1), - 'title6':CSize(1.1), - 'title5':CSize(1.3), - 'title4':CSize(1.5), - 'title3':CSize(1.7), - 'title2':CSize(1.9), - 'title1':CSize(2.1) -} - if platform == 'android': from .widgetExt.phonebutton import PhoneButton from .widgetExt.androidwebview import AWebView @@ -162,10 +152,12 @@ class SwipeBox(SwipeBehavior, Box): class Text(Label): lang=StringProperty('') otext = StringProperty('') - def __init__(self,i18n=False, texttype='text', wrap=False, + def __init__(self,i18n=False, texttype='text', wrap=False, rate=1, fgcolor=None, **kw): - fontsize = font_sizes.get(texttype) + app = App.get_running_app() + self.rate = 1 + fontsize = app.get_font_size(texttype) * self.rate fontname = font_names.get(texttype) self._i18n = i18n self.i18n = I18n() @@ -181,6 +173,8 @@ class Text(Label): kwargs['text'] = kwargs.get('otext','') super(Text, self).__init__(**kwargs) + app.bind(font_size=self.reset_font_size) + # self.bind(texture_size=self.setter('size')) if self._i18n: self.i18n.addI18nWidget(self) if self.wrap: @@ -190,6 +184,9 @@ class Text(Label): if self.bgcolor: self.color = self.bgcolor + def reset_font_size(self, app, fsize): + self.font_size = fsize * self.rate + def resize(self, *args): if not self.size_hint_y: ps = [0,0,0,0] diff --git a/kivyblocks/blocks.py b/kivyblocks/blocks.py index 92a6f24..e813659 100755 --- a/kivyblocks/blocks.py +++ b/kivyblocks/blocks.py @@ -214,7 +214,14 @@ x = ClassX{klass_cnt}() print_exc() if errback: return errback(None,e) - return None + return { + "widgettype":"Text", + "options":{ + "text":f"{url=} error", + "wrap":True, + "halign":"left" + } + } else: config = getConfig() url = config.uihome + url @@ -727,7 +734,7 @@ x = ClassX{klass_cnt}() print('Block3: desc must be a dict object not None') return None w = doit(desc) - print('widgetBuild():w=', w) + # print('widgetBuild():w=', w) return w @classmethod @@ -769,7 +776,7 @@ x = ClassX{klass_cnt}() 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)) + # Logger.info('children=%s', str(children)) for c in children: ret = _find_widget(name, from_widget=c, dir=dir) if ret: diff --git a/kivyblocks/blocksapp.py b/kivyblocks/blocksapp.py index 9de0825..3965f07 100755 --- a/kivyblocks/blocksapp.py +++ b/kivyblocks/blocksapp.py @@ -12,6 +12,7 @@ from appPublic.folderUtils import ProgramPath from appPublic.uniqueID import getID from appPublic.rsawrap import RSA +from kivy.properties import NumericProperty from kivy.factory import Factory from kivy.metrics import sp,dp,mm from kivy.core.window import WindowBase, Window @@ -48,6 +49,7 @@ signal.signal(signal.SIGINT, signal_handler) class BlocksApp(App): + font_size = NumericProperty(24) def get_rotation(self): return get_rotation() @@ -78,7 +80,26 @@ class BlocksApp(App): if isinstance(v,dict): register_css(k,v) + def set_fontsize(self, x): + self.font_size = x + + def get_font_size(self, ttype='text'): + text_fontrates = { + 'text':1, + 'title6':1.1, + 'title5':1.3, + 'title4':1.5, + 'title3':1.7, + 'title2':1.9, + 'title1':2.1 + } + v = text_fontrates.get(ttype, 'text') + return v * self.font_size + def build(self): + tl = Label(text='test') + self.font_size = tl.font_size + print('##################app_font_size=', self.font_size) config = getConfig() self.workers = Workers(maxworkers=config.maxworkers or 80) self.workers.start() @@ -93,10 +114,10 @@ class BlocksApp(App): self.default_params.update(config.default_params) self.public_headers = { + "client_uuid":getID(), "platform":self.platform } # Window.borderless = True - print('Window.dpi=', Window.dpi, 'Metrics.dpi=', Metrics.dpi) Window.bind(on_request_close=self.on_close) Window.bind(on_rotate=self.on_rotate) Window.bind(size=self.device_info) diff --git a/kivyblocks/boxViewer.py b/kivyblocks/boxViewer.py index a6be19c..cec3d22 100755 --- a/kivyblocks/boxViewer.py +++ b/kivyblocks/boxViewer.py @@ -29,7 +29,7 @@ from kivy.graphics import Fbo, Color, Rectangle from kivy.properties import NumericProperty, StringProperty, DictProperty from .responsivelayout import VResponsiveLayout from .toolbar import Toolbar -from .paging import Paging, RelatedLoader +from .paging import RelatedLoader from .utils import CSize, SUPER from .ready import WidgetReady from .baseWidget import VBox diff --git a/kivyblocks/clickable.py b/kivyblocks/clickable.py index e9bf4c7..7256d97 100755 --- a/kivyblocks/clickable.py +++ b/kivyblocks/clickable.py @@ -3,6 +3,12 @@ from kivy.logger import Logger from kivy.uix.behaviors import TouchRippleButtonBehavior from kivy.graphics import Color, Rectangle from kivy.uix.boxlayout import BoxLayout +from kivy.uix.anchorlayout import AnchorLayout +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.relativelayout import RelativeLayout +from kivy.uix.gridlayout import GridLayout +from kivy.uix.scatterlayout import ScatterLayout +from kivy.uix.stacklayout import StackLayout from kivy.factory import Factory from kivy.uix.image import AsyncImage from kivy.properties import NumericProperty, DictProperty, \ @@ -395,6 +401,34 @@ def build_cmdbox_view(desc, rec=None): x.setValue(vd) return x +class PressableAnchorLayout(TouchRippleButtonBehavior, AnchorLayout): + def on_press(self, o=None, d=None): + pass + +class PressableBoxLayout(TouchRippleButtonBehavior, BoxLayout): + def on_press(self, o=None, d=None): + pass + +class PressableFloatLayout(TouchRippleButtonBehavior, FloatLayout): + def on_press(self, o=None, d=None): + pass + +class PressableRelativeLayout(TouchRippleButtonBehavior, RelativeLayout): + def on_press(self, o=None, d=None): + pass + +class PressableGridLayout(TouchRippleButtonBehavior, GridLayout): + def on_press(self, o=None, d=None): + pass + +class PressableScatterLayout(TouchRippleButtonBehavior, ScatterLayout): + def on_press(self, o=None, d=None): + pass + +class PressableStackLayout(TouchRippleButtonBehavior, StackLayout): + def on_press(self, o=None, d=None): + pass + UiFactory.register('checkbox', build_checkbox, build_checkbox) UiFactory.register('cmdbox', build_cmdbox_view, build_cmdbox_view) @@ -409,3 +443,10 @@ r('ToggleText',ToggleText) r('ToggleIconText',ToggleIconText) r('ClickableImage',ClickableImage) r('ToggleImage',ToggleImage) +r('PressableAnchorLayout',PressableAnchorLayout) +r('PressableBoxLayout',PressableBoxLayout) +r('PressableFloatLayout',PressableFloatLayout) +r('PressableRelativeLayout',PressableRelativeLayout) +r('PressableGridLayout',PressableGridLayout) +r('PressableScatterLayout',PressableScatterLayout) +r('PressableStackLayout',PressableStackLayout) diff --git a/kivyblocks/container.py b/kivyblocks/container.py index a41228c..70b2c9d 100644 --- a/kivyblocks/container.py +++ b/kivyblocks/container.py @@ -1,7 +1,12 @@ # container is ScrollView with a dataloader -from kivy.uix.scrollview import ScroppView -from kivy.properties import DictProperty, ListProperty, BooleanProperty -from .paging import Paging, RelatedLoader +import json +from appPublic.myTE import MyTemplateEngine +from kivy.uix.scrollview import ScrollView +from kivy.properties import DictProperty, ListProperty, BooleanProperty, StringProperty, NumericProperty +from kivy.clock import Clock +from kivyblocks.blocks import Blocks +from kivyblocks.paging import RelatedLoader +from kivyblocks.widget_css import WidgetCSS class LayoutBuildFailed(Exception): def __init__(self, desc): @@ -13,7 +18,7 @@ class LayoutBuildFailed(Exception): def __expr__(self): return str(self) -class Container(ScrollView): +class Container(WidgetCSS, ScrollView): """ "loader":{ "page":1, @@ -24,43 +29,118 @@ class Container(ScrollView): } }, """ + idField = StringProperty('id') loader = DictProperty({}) data = ListProperty(None) layout = DictProperty({}) viewer = DictProperty({}) + min_threhold = NumericProperty(0.01) + max_htrehold = NumericProperty(0.99) extend_x = BooleanProperty(False) content = None def __init__(self, **kw): + self.dataloader = None + self.engine = MyTemplateEngine('.') + self.id_widget = {} super(Container, self).__init__(**kw) - self.content = Blocks.widgetBuild(self.layout, self) + self.content = Blocks().widgetBuild(self.layout) if not self.content: raise LayoutBuildFailed if self.do_scroll_x: - self.content.bind(minimum_height=layout.setter('width')) + self.content.bind(minimum_width=self.content.setter('width')) if self.do_scroll_y: - self.content.bind(minimum_height=layout.setter('height')) - super().add_widget(self.content) - self.dataloader = RelatedLoader(target=self, **self.loader) - self.dataloader.bind(on_deletepage=self.delete_page) - self.dataloader.bind(on_pageloaded=self.add_page) - self.dataloader.bind(on_pageloaded=self.update_tailer_info) - self.dataloader.bind(on_newbegin=self.clearRows) + self.content.bind(minimum_height=self.content.setter('height')) + self.add_widget(self.content) + self.viewer_tmpl = json.dumps(self.viewer) + self.bind(on_scroll_stop = self.on_scrollstop) + if self.data: + d = { + "dir":"down", + "rows":self.data, + "total":len(self.data) + } + self.add_page(d) + + if self.loader: + self.dataloader = RelatedLoader(target=self, **self.loader) + self.dataloader.bind(on_deletepage=self.delete_page) + self.dataloader.bind(on_pageloaded=self.add_page) + self.dataloader.bind(on_newbegin=self.clear_records) + Clock.schedule_once(self.loadData, 0.5) - def delete_page(self, ): - def add_page(self, ): - def clear_records(self, ): + def print_info(self, *args): + print(f"{self.scroll_distance=}, {self.scroll_timeout=}, {self.do_scroll_x=} {self.do_scroll_y=}, {self.scroll_x=}, {self.scroll_y=}, {self.content.size=}") - def add_widget(self, w, index=0): - return self.content.add_widget(w,index=index) - - def remove_widget(self, w): - return self.content.remove_widget(w) - - def clear_widgets(self): - return self.content.clear_widgets() - - def add_data(self, row_data): - desc = self.transfer(row_data) - w = Blocks.widgetBuild(desc) + def parse_viewer(self, rec): + return json.loads(self.engine.renders(self.viewer_tmpl, rec)) + + def loadData(self, *args, **kw): + kw['page'] = 1 + self.dataloader.do_search(None,kw) + + def add_record(self, rec, tail=True): + desc = self.parse_viewer(rec) + w = Blocks().widgetBuild(desc) if w: - self.add_widget(w) + idx = 0 + if not tail: + idx = -1 + id = rec.get(self.idField) + self.id_widget[id] = w + self.content.add_widget(w, index=idx) + else: + print(f'create widget error {self.viewer_tmpl=}, {rec=}') + + def delete_record(self, rec): + id = rec.get(self.idField) + w = self.id_widget.get(id) + if w: + self.content.remove_widget(w) + del self.id_widget[id] + + def delete_page(self, o, d): + # print(f'delete_page():{o=}, {d=}') + for r in d: + self.delete_record(r) + + def add_page(self, o, d): + # print(f'add_page():{o=},{d=}') + dir = d.get('dir', 'down') + recs = d.get('data', []) + tail = True + if dir != 'down': + tail = False + recs.reverse() + + for r in recs: + self.add_record(r, tail=tail) + if self.extend_x: + self.scroll_x = d['locator'] + else: + self.scroll_y = d['locator'] + + def clear_records(self, o): + # print(f'clear_records():{o=}') + self.content.clear_widgets() + self.id_Widget = {} + + def on_scrollstop(self, o, d=None): + if self.dataloader is None: + return + if self.extend_x: + if self.scroll_x < self.min_threhold: + self.dataloader.loadNextPage() + if self.scroll_x > self.max_htrehold: + self.dataloader.loadPreviousPage() + print('return here ...') + return + if self.dataloader.loading: + print('loading is True') + return + if self.scroll_y < self.min_threhold: + print('load next page ...') + self.dataloader.loadNextPage() + if self.scroll_y > self.max_htrehold: + print('load previous page ...') + self.dataloader.loadPreviousPage() + diff --git a/kivyblocks/defaultimage.py b/kivyblocks/defaultimage.py index ef2354d..8b1008a 100755 --- a/kivyblocks/defaultimage.py +++ b/kivyblocks/defaultimage.py @@ -1,3 +1,4 @@ +import os from kivy.uix.image import AsyncImage from kivy.factory import Factory from kivy.properties import StringProperty diff --git a/kivyblocks/eventhandler.py b/kivyblocks/eventhandler.py index 9f07f54..37dc6c8 100644 --- a/kivyblocks/eventhandler.py +++ b/kivyblocks/eventhandler.py @@ -1,11 +1,11 @@ import inspect import asyncio -from functools improt wraps +from functools import wraps def eventhandler(func): @wraps(func) def wrapper_func(*args, **kw): - if inspect.inspect.iscoroutinefunction(func): + if inspect.iscoroutinefunction(func): return asyncio.gather(func(*args, **kw)) return func(*args, **kw) return wrapper_func diff --git a/kivyblocks/i18n.py b/kivyblocks/i18n.py index 5ad2fc5..1b35731 100755 --- a/kivyblocks/i18n.py +++ b/kivyblocks/i18n.py @@ -58,9 +58,12 @@ class I18n: if config.i18n_url: url = '%s%s' % (config.uihome, config.i18n_url) hc = HttpClient() - d = hc.get(url) - if isinstance(d, list): - return d + try: + d = hc.get(url) + if isinstance(d, list): + return d + except: + pass return [] def loadI18n(self,lang): @@ -74,9 +77,12 @@ class I18n: if config.i18n_url: url = '%s%s/%s' % (config.uihome, config.i18n_url, lang) hc = HttpClient() - d = hc.get(url) - print('i18n() %s get data=' % url, d, type(d)) - self.kvlang[lang] = d + try: + d = hc.get(url) + print('i18n() %s get data=' % url, d, type(d)) + self.kvlang[lang] = d + except: + pass def __call__(self,msg,lang=None): if lang is None: diff --git a/kivyblocks/imgs/.DS_Store b/kivyblocks/imgs/.DS_Store index 90b7b9386e365f534d9f17790330bb1a48637278..7f6403d500f0a81e65d02c448c22be036dc97ad7 100755 GIT binary patch delta 62 zcmZokXi3-*tjy2NkiwA4kORaeKwPmoLAjiTF=g@@HHFCyDlD7-ssCr${9EM?6GCkA HKXEnyaB>xB delta 64 zcmZokXi3-*tjx>BkiwA4kORae45^a?1jHs62(fIgQI2I{&SOZKd`?Ya^DlK~*3JJ_ NZZW|`CjS!W0sw4;6oLQ% diff --git a/kivyblocks/imgs/add.png b/kivyblocks/imgs/add.png new file mode 100644 index 0000000000000000000000000000000000000000..92c7aa466c463484d4485a130b86395189247e97 GIT binary patch literal 10592 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%0D6r3Uo41%}n{z=+O#2-pMJ_$>$eNHqeu`3AhbX zTJ=dnl!z@bUKx^p6;C_M!?9D1HQpo;${Q(5J7fQXD2bvsSdVQq<5NueJJ2}ca)jzI zyYFfk&N$++)aBbOwbMRZ+uV1$D)0VD^A6u6BiC&F*F_q0i! zK`ozZd)g;muWNs&0(;?a?xT2MpB)sBi9rkF#;lqVSA&SJ|CX9sNADW}<82F}YdXUo zI8-yVt*>8BJv;u=RGaq!wY=-x^I+#`-Zm#-5E6K^_2BL&AF{rwE&mo+ahn3CLj4lo z`xRzck^%GH4tQ)-+r$jz>lDu#$WI3!;-5owvI2;B#)YzkJ~R1Of2QJqxzW3&yoHWR!aKHh2tfkmU$MH}98LvE~)jyMg}^1<`0 z(lq3v*v}V?N}wVp;ky(toN`Acs2ELEL4oA$Szl|>dw!(YwNF`zK=->TB!teN87jK7T?UlR)6C=%i|FPn=N&J#!F(rPfHY>_WgBZCkjeQ5c^`~jJ_JME!Q3t2@XPq zHJo!~S=Z=u<$E<^q!v|RZ^}TM9~Z?#Xqc6{SXaihuxs*MsFx*y)8P>Ht@q0vWq&eU z5KbUrdkgC%(AD%_)*2YIPDi!`W4MlvUfC-%9K5_cDFKleWrGdJ-DVj==)X{wI-0re z_pi@O5J&LNuMAIgo7^0RM%7fhH>JOv0DD5_=!Gt+b|J89iHttOd3FT-%;VV?YW6wZ z%efK|8h(9jy6e^0=f$Xbz7^C7C|d40jjkA#gukl1e_mThhWio0vpQ)jaHJ?&qJlMj z*fr*XpH=Z((b-G zPw99hw60w^F)92I-DvzX3aD98?GwD=%;RR;Wq#KRkRZaxV3oJM{NvkD>D!a`^Z*JO zt9W2?6d+%339RQ7OY}=F}g#hOA40lP#)C9 zA}0e9!)mm)JPiJLbUlJWB0jM4V%X%tHK|HA)13Jj)S6O==f(;0s-Z6O4EeB0GJ;tf z9UEo11{Titf=0*m@b@i)YKYge?GN*#U0hrc-jdXbvdVb1Djo6S){zfd9~L$bs`(*- zky*lgz_vZFaP@-ndz5_>0mZGl?>8l#o1o4k07E>7xRI@;ynzI4k#j9Eg?aAU4U>h- z6fC~vG#$EUruSU@rTTL2Y`m`#v~3C?>gD+9e?&jKYS_B=!%M&O?E6OLI}NVqA1otY z;lv=vj=QgMUZl`azGt1m8t)SQHOV<@g`vq|Ty-tVOmzW+4nOaX*2r7>U&NMIB&p1>f z^gK->@|w^nLd6OPjc#1Mk?8dK?#*L&7a7vl#y-C2_fPiikc4{9blbKU_is3^K5=7T zpynxYB}PJvAWg46>`8t7>Mi&67w+S(DZ1c%-$&+ooLB=f8ewjf_7c!vN!9$D~Xhv9V=WKi5+B^3D0fY+4E&u=k literal 0 HcmV?d00001 diff --git a/kivyblocks/imgs/delete.png b/kivyblocks/imgs/delete.png old mode 100755 new mode 100644 index 4f1f30ad0f67c4312bb151cfc6dae539ec3f0b73..fd447fbb3bfa374c29541c6c8e2e537497728121 GIT binary patch literal 10410 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 bnuvNr-eAyT){X&$YGLz+Z59vLGb8^GOH0l6 literal 13894 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 z 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 G=KlfE0^ETB diff --git a/kivyblocks/imgs/deletex.png b/kivyblocks/imgs/deletex.png new file mode 100755 index 0000000000000000000000000000000000000000..4f1f30ad0f67c4312bb151cfc6dae539ec3f0b73 GIT binary patch literal 13894 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 z zn>z314h(; z6mmaU^PJC0c4S0aO14Og3lc-+4Yboy?_FKK{(&{>UnUI_SaB$YRj1bGWzm>qQ8}YQ zm&Q>^O)HP3W@qPx$Vw5~Nv|y#Ke;ZvyZ0A7^w-$$MsXpm@mSq9Hs&tRzm*{_E_YU} zCu}?qeD_!yb2J%hVMoMVHMTSCFCLSWwyxZ+E0>hjr+`a(Q6d!%85}6qZ5;|i&=*mA z8A#~E#R&dj>T-v11$)~=rJzn;c?3OqGO}9<|((o1XljGaCgMb zgwFZI9nzNgjO4~M(VY+vD{_t z-IDlvC)IfTnQ!1%C(gU4J*?oQt9P)QDFso!C~K-6!k&iEEGi_ebLOPao7!y8Pn&dG zO03mjrUd% zSZI$sn}3~z{GYc+hM9oMoOZuJK|k>{1U3OKUj3xDb~Mkm>gBRT$K8!%CLnPNdE+yT zIJANV|7Dg*Bs3e;$G$h$omzq(=@ywtc;%N*=$S1iXiyq$=%2BxD$R|7STXt z)3T1!9;kJ~IvC=s%B|_w#il<}y>+ZMxxi8HB{y2(+K1+9pc35157MAqt3_BVpciCf!^}}~pYSig7Z=}MZ@%Jw>qL8L zXmR!~f=k~$`s11DxyzLwXZ8~3o%_oD3qz{zNnYK;C+WS}3c*xCB~rDP`O(gR$Bh$s z0yP=Y*L_ zV2f=Di1(V66IzI6(K2!u2RNJD@LB5Z91V|G7r!RfT*JpgZho}&?0s0gqPgy8Q4+o$ zA75`L&Kh&YRx*SvG-02_1*--|_s5cnfVOSp2q3GUTv%^juDiSa+)1!4(Fbh%rh^@X zQ$6S@K4TVDl#ngXwmd|{w)oe_yWk&tG`7fW3n3FD(<24} zoDGE*A0SaFuMCUW&5nviR!2;nGf6XB*#J@a>zfUym_R$Nbx8$s{L9*|@B9&?K*Bht^o0t$HpW4u=`e{u2{)d)(r8qp6KAP;{>^ zn;(nf1hmaiRlaeQ%&v-8RlpZH$(Q=dd_y6CLV9Y;kFd=bxS6R_@6C#PB3wi%ayM4Kc@cyTwi1mCyCRfNH(1Qpya-EvT|9y^d2wMjut!w^VeImPC_uxbud!RKm@IH^L9%7 zq!&-Q3!7_rmRhV1Vso&hxrI9)9SA0-mn)CFT(93+9OQP%Ep5~IkfG(ez`qO=S$|fA zpDH+h$q|1TD1m{j=o1r|Gi7QbQGfk@+QF69kCvs8Mw8wA)FB_)lwCkn9-Ke*Vhc`_ zbm%=0{wa~jN>P=y?E0TRf&H_y*~w*uP;(f_n{3+63tm>>%qMmCZU;r&CQZ7U%rt!taOI&NSv@-Rew@qw=B>e>X%&;TSYd}jW`N# zoKZ1t+>ZuP3Y>17w4J}B!K&#?Tl=odX=_$~x412xZRZ}=CGvwciWTX1DGb((IDRV5 zRpzKxx-I;&q3_tUvvdPe&QSMSEiWmMopaZ#T41Hw-XjJ|=mkLDyK&|-3}Wd@*h_v| zmjB6Zab9Jw6rnDT@R-K~F6Y9IZejzc>~V?kBSw3?O2~iTpB5iBMmVemX6dGxK5@W3 z5v(rId|2~$&{9+~P?)HRSn&{}Pg6jIbP4Hv8SlE-8qzFge^plp35bORuz;d0XjXPz zXLe;XjzwHz$ymI15`>qC#%Vl1#Qi)Df*YsFCAl6!-JU7Mt;JWC3bqhL&p`1lrVv{z zM#8&|?+>XGCKd}z(bX|8*-27g1c1hmug0vju=tRlePvlGGY*@9)y;f#7eE~dif`Jk zeQ29eB90A9>AFn3bYF6q|D}xi!4rOtGTf-pE0AlRnOqmx?!ePG?GQpV$cD^lMz)VK zq7bV41%9{Mo3cRy5OUnB&!`XZ9ria7?){y;Ete_F+$fOb1L1eh=X01ory>k=w&|m< z{(YIz7m90vF08eCAkZ9s*}uQoFCf0Qt}KW^tgH8iw31kbOJWOf2E1P0h8-mUT!9eh zHQYl3c!49x-gH+&uQRZ-;5KC2LC=9h7(rAQb|FGML&!~+4u)>sK{dv|Zc8FRE+=FH zOwzL~jranp9n1c@q>%7ZLdBlfW}>VBhpei0%NA zUwo9Ivmyj{eBX4gi@CHcngkC+1wz2l&`%R}g}Q^vG~u)ILLp?kxnmL4mtm>xy zHj35Fj1uksn_fx@N{Nu58BzwNl=T{re8V`u)`GbOM;Q{oxhj^t#@4{C!A zG=jme2i9W$50+$^x=Pzdo7#V~Qmy?as!}(nZL;qYvA9Drg?)MTI7#GEK+C4coSi4D zmeo}?@R}Q4#C}-+yD2s-x)o0einD4u~_8C^fXWz0-S%6tyFa|2z3% zt0AibVVh1K@v8G*ICq5X!7h&Pkki{oLPB3<@6U7u-dNxTGWh&vJAX_&2&tAQRTn_? z*=wLl+PhamGhGpu=3W$WKOG679Xl?9HNLN{#V^4gF3sD%4q})*^uw=hIAOwEJnHbh z3$0H$Pll6Xy@zLZcFf}98&sj zuWAy&2QO2s%}Ey)S5>MZIf`xR!~1_PRt)Se0mM`kg-PPGK^S zYtrxBb+T;9ug`{GPK2nZZ93=~Yxcj+BF-JUJ}rWY_S}!VLlFU_f;x9wk2D79s;5)w zlM`)1`TYpx+Jc0*h7~sst=?~D!B2mmpGeMA;%fBd?eiodn z*xP85?uDwhb}LOZk7g|%mHPwz%_9`+&YDqtkQlI@LfIc@GD&|z)CJRjOw`%pF+up{ zqivk0p3v3Ib$au`uk)``b+PDxAmJ%6oW@FnGe=PM%L=S5FeuO;nkEam2a9|C_ul3J z1a<_sM^F0`f^f~z?*beQV9#VTF}<%Y>ZJ<8u6c`5^c7?ATf3-1l>0aO<*_(8D*BPF zgPiMdW$+3~UL;48lmXjs^YeN85z<7z>)@vD!hV%h?z91WO%lsN7Y!0rKqQS=(XVNM zmqYqMY7{0liS#5_-~CWN7yYzlnDrhIQktvp&XUo$Vm>qKH%x;lkNBFF@Nqx|MSrHW zi*0Cq!UE&j+j>v2b*?|%g9UwK{6ne+rqkzAj}KsmBaMt<^+J``P8KB*2us1wR52NF zR2fb8q!ZwB)+@Sr(Yp9kY@(+j;60VU5-&EL5jY#3k?r0arkeTV_D&Jz`3BmY9UIEe z*EE4Q0t)}U1b56bqB;g1E6j$lVFW74KkdHu`I109p|3ZNjZGAyw?V?e*|@#w^V14K z0)Iro1o=3EK}V~-5LON*&?USU(BfOJg4ai?6bSVJopSp+(36f$9y-|DbKh^acxE)6 zsq^7>agQ57t5TjOHyeoyl6<$kP?v*!a@-kpwIYPIso2{&ay7W7>Kh9v_CpRHm_0iL zR)`LQbmcX&em)idVC`ftM-YWTi!}`_qYL!b*!xxlKr4 ziQ=ji0YeTy{wggoo3mbKZeCWj(po~{^#HW!ck=yhO7R}!LTWv%=E z=o^!hf;rCdC0<)xjWmRwVCD-l~v!ivKL_962LFhCPJ58PK|Uw}eJI2{t^S zWNWG=Gz6_}b< z%HwRc)!Jj{S37%|GxJiz@@MiTLTD$Bc2d1HNiNDR+C&IdM1`FSE8Kn4iw82DR5d9% z{-?72!7%DB-F%dVCfYGtj@P8f3T`4sn*vcw3S$oczm#tM%mH@Pa6)xV0*U~GBE{KM zp$#?Jr7gRXYjrjGVnm_BRmC-CCtksHDYMPG|FO+(Lqs+y_zrsm<#ENsg1T7pSH2K} z!E333XCOWtzOlvVwLiUGwoup8F`69Kb~hz~5N+|v)a^EeUH1H>KP3o=h@gCE(j!_p z$qTm1OFU@Ya7SaNmWJZ^2Uk^y!0B-$jHlBi-irY>-EVTwE^dtN2+>F?h1j zzH~f#x=Rzp_*2i>Vexl$FG*ej9U)QyI)JC=2VEoAuemzUZJwa!|2T;ceG>ap7=zjH z=R(Ujv(W1KEM{gvUI>&u_b(&x)xCpO($q5O!3s3>tr?FlpV@As-qYWp&S%VUmJJmn zIeZ+2VE47~;C+TmYHOfiyb1g|Rul91t}G*nBs};MIN+|e5?{Um71l{LvWruJYaCsg zAM5xQgq&M{)R70TUzisTb5vZ*xR{ZwLqLDL4%6HOS|-*?m>BxMMB?mQKdEn4JRJ*z zkf!d8g-m$$OoN_cE`;dV?zf|!Y(=|Y>|sOiIx2+E6G_N4rJf+fPaVHWfNDUM?R_dA^P-|$nn8ZK;K2@M8KEELxSM>1MKqM1dIDHO|p(u9I2aoGH z!Yc)gi)6d)fRhqG)$WLFR5&77trO*I12N=%Pe0na7|1+W-aYRFeJo3ggw_f)%a*w9 z%1vNx`Bz^(_Ix>+7}8eiWBJ~~^jqBdr}@_E-Fy+}2kfLapAsSA4SvcnNoW9GQIGwn z>A|Y)q_^UlO?zUf6%hZq{u{+-dvJFgq|N>w%|-VO=o?zm3R% zoCm7(0;tk6m)@HE1r_Pnl(e{N0fM8Xx`$P-+*BQPDD>$KTedlVkUlHdd6;$DM^mR3 zARne+EL+phaQ%ygGd=IqJ5dENeam=(0W+kq4&FavP+Nwe09?&j_DcK+?=}9gk+#P7jw24R~6i&M&7Xmb|-6s>_amzMI$~jwbochEcvi^#h&<;zh{k){kaElh4%dj1cC+!hb4+5e%t1@DtNv6Q0hsl<+#U^-W%}&b8bOm<%6fMg-}vZ_aU#_ zYafU6^63qkk?ANm^}5R4nxyfacf4<4lzJZ(rM2QRQ;)UD%_V@e4$nv*UHW#fp$w$a>l?dg(;?i$>novEzW5$X8tf#=C?@>L+zA zBVfOUho3_4e#nK}KI<5e6katB2hcZcNlX)WH^k%IW!>H#%d=k`Rm|1sAlJ#R1P86$ z&HDlJS#|Nnw*xy5om^;YI+?Zxtqq~!Pu*|p!kLFJT*Gd{im~>D!Ehl&v;#;`ShCIw z{QbQoyp?_sRM+@y;m#i!K%A^AyZRd}8zeFsp&G&=fn!yv-VN* z7!e~Q&B{yv(L|SBh9$6HF#P-WvyoXs2-Z`m0#DBJQv z`Sd)trmFouRbX{`a7rgmgMbI|kZ?lMFgpk#TpXeIi;{EUqLIL8&ZC!KU~vyUcW3(m zJziL~T>SKac8^XL!jVs%mAl0iynlvB8l91?6*9;NXZW>wx2hVBm^TcK+*ZOzjdTol znz8gA)0oUvq*`E5f^5a&7Nk6&-VD5M?F=ckKxUFfO= zOHhmq!2l{WgYH9~wHyd~v3=#{IMvh{Dgwz(q9VSG@SMkz!{V=_7+lmvWr*&#c>oeT zb!&lcOC!YtCopTea^BIN*Q$d&&lBLMa{ZE(Ljxdip{@oP{W6H-FZBcUlUJO83cT={ zaMh{o;M;0xz{dAoC^`-t>xEZhr3p&cgUzyGlei|8mD@`rhkep_95Z0HfTqOGxX2z#ieVzZRmt%KxqAM8y$m7chBgl zmc0^Oxl*T?cgh!(=$kXlKb=ZAD7DA_n##j zQcr!X`o1Apl9#6&L#ttC++|iv;9Zt?J0oiPEv2Mtgrn2p{kKnf@@!EO?zu?Fj}5(c zadXWG*WsEkcw!&4OWd$QFF}O!H6w7On|3A`Vd)kkNNMWZAmyFIJvE6(Y$l(9Q=gQu zT6!0JP{?u$Qrtt zu!Fa1dT~+aY@~Dp0O7;(Q(I3U_3gSWwEJ#d!3g>U%sHB2ti*;R>UEBD$#~B_0K)Wa zu%zOnc_m$4JZFb%j6~2O0O!Z~)`NUFt2&qeml_;#m426v3q}#U$?9i@R&U(~nlmn3 zpN&Kmg$WHN@Q=2mq&j&h4}*TmUi`$P?}Kas7o;r5U-23U~^ykm3`_LXcRceq(R&bV;}B*p2Lk+o5B_JvR(db zTgV&D+pN`1wT<|TY((thZzFe4cBCl3P^fT2fbMtA?&>fXI8;z_pr#&ABmko!CY%^H z-vW0MU1Dp}oBv$7{5%erli@ zvmr*`hapN6PVyXsyQRIkTrbW&nQlm{QEyT&f4Kvf2Jty9S-DOKxj!1qB`rQtcNb_I zO{Jo+|BP}G-x;`$Ru7CUx@@3I9;JlBeF%=;HCn5>ySuQNFh8~SuZ#?O1UCNLCEpnw zT}+7Zdj^SGnQUmf58VTQIQl-vE%WgvD5GzwM7@A5zX;p%W#X@)#W&oBMhv+^XRrvW zo~@GI4g|Qj-bdH-ON#Yg;jD(1pwp81vI2U)J9JCoLMrUoRNun92rqJwe!sB@62M>T z5nDSvwx5K@mngA3*~5b*@C@fj6?QE0H)fU;_5wi8*S>GOjiC5rm{Bm|^q}ip?~>%z z=|;4Hfdbb6e61U~!sM}#sz>&x#87GG=ghcP>J9H3SM3_r+)ep#)jGLLzDjH13ig{Z z!aWU=DZbD9w8QTMIIG}D%i$-wk9$GvPa13l&*5cw9fUZIM^+SmmE%o}@3Mm#hTyhQ z-92-3YR(YgZ`LPgxC_1=*iY~Q#dNXNap9vrS#`z#owM@Tw^(L`BQm~rIOTmu6jr+L zWmnfZfl8_R4pAn!#a|KyN~?P|*Y$$XamH;=?I&7T%>`~z^Um%wzl*aH^1tjpDVWX;(BACOz?-+Hu#``#2sK0KCkCuHfswH6Q2f3`^-TCp5#%R)YfRalpyr zHQa+|Nrzon!vi3-e2X-5t0b}JEC(!J4e&% G=KlfE0^ETB literal 0 HcmV?d00001 diff --git a/kivyblocks/imgs/lensid.png b/kivyblocks/imgs/lensid.png new file mode 100755 index 0000000000000000000000000000000000000000..3a0e0c70fee8988147c789ab39b197587baf0589 GIT binary patch literal 17755 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 Nkg7T=g;y*B|1Wh^riTCk literal 0 HcmV?d00001 diff --git a/kivyblocks/pagepanel.py b/kivyblocks/pagepanel.py index 93c4aa5..a151474 100755 --- a/kivyblocks/pagepanel.py +++ b/kivyblocks/pagepanel.py @@ -177,6 +177,8 @@ sub-widget's description file format self.left_menu = Factory.Blocks().widgetBuild(left_menu) self.sub_widgets = [] VBox.__init__(self, **kw) + app = App.get_running_app() + print('========================barsize=', CSize(bar_size), bar_size, app.font_size) self.bar = HBox(size_hint_y=None, csscls=bar_css, spacing=CSize(bar_size/6), diff --git a/kivyblocks/paging.py b/kivyblocks/paging.py index 49af829..f6f395a 100755 --- a/kivyblocks/paging.py +++ b/kivyblocks/paging.py @@ -17,28 +17,6 @@ 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 @@ -113,11 +91,11 @@ class PageLoader(EventDispatcher): p += 1 self.total_cnt = d['total'] self.calculateTotalPage() - d = { + d.update( { "page":self.curpage, + "data":d['rows'], "dir":self.dir, - "data":d['rows'] - } + }) self.dispatch('on_pageloaded',d) self.loading = False @@ -129,10 +107,10 @@ class PageLoader(EventDispatcher): def loadNextPage(self): if self.loading: print('is loading, return') - return + return -1 if self.total_page > 0 and self.curpage >=self.total_page: - return + return -1 p = self.curpage + 1 self.loadPage(p) @@ -163,39 +141,18 @@ class PageLoader(EventDispatcher): 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.buffered_pages = 0 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): @@ -216,13 +173,15 @@ class RelatedLoader(PageLoader): self.calculateTotalPage() def doBufferMaintain(self): - siz = len(self.objectPages.keys()) - if siz >= self.MaxbufferPage: + siz = len([k for k in self.objectPages.keys()]) + self.buffered_pages = siz + if siz > self.MaxbufferPage: if self.dir == 'up': p = max(self.objectPages.keys()) else: p = min(self.objectPages.keys()) self.deleteBuffer(p) + self.buffered_pages = self.MaxbufferPage def deleteBuffer(self,page): d = self.objectPages[page] @@ -237,18 +196,25 @@ class RelatedLoader(PageLoader): def show_page(self,o,data): if self.objectPages.get(self.curpage): self.deleteBuffer(self.curpage) - else: - self.doBufferMaintain() + self.bufferObjects(self.curpage, data['rows']) + self.doBufferMaintain() self.totalObj += len(data['rows']) + if self.dir == 'down': + data['locator'] = 1/self.buffered_pages + else: + data['locator'] = 1 - 1/self.buffered_pages + super().show_page(o,data) def loadPreviousPage(self): pages = self.objectPages.keys() if len(pages) < 1: + print('self.objectPages is null') return page = min(pages) if page <= 1: + print('page < 1') return page -= 1 @@ -257,68 +223,12 @@ class RelatedLoader(PageLoader): def loadNextPage(self): pages = self.objectPages.keys() if len(pages) == 0: + print('self.objectPages is null') return page = max(pages) if page>=self.total_page: + print('page > 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) - diff --git a/kivyblocks/register.py b/kivyblocks/register.py index 77c8d61..9f1c87c 100755 --- a/kivyblocks/register.py +++ b/kivyblocks/register.py @@ -8,6 +8,7 @@ import kivyblocks.clickable import kivyblocks.multi_select from .baseWidget import * +from .container import Container from .tree import Tree, TextTree, MenuTree, PopupMenu from .toolbar import ToolPage, Toolbar from .dg import DataGrid @@ -53,6 +54,7 @@ from .landscopehide import LandscopeHide r = Factory.register # if kivy.platform in ['win','linux', 'macosx']: # r('ScreenWithMic', ScreenWithMic) +r('Container', Container) r('LandscopeHide', LandscopeHide) r('VideoBehavior', VideoBehavior) r('ModalBehavior', ModalBehavior) diff --git a/kivyblocks/scrollpanel.py b/kivyblocks/scrollpanel.py index fb3ab08..b8f2aab 100755 --- a/kivyblocks/scrollpanel.py +++ b/kivyblocks/scrollpanel.py @@ -6,8 +6,8 @@ from kivy.uix.widget import Widget from kivy.uix.boxlayout import BoxLayout from kivy.factory import Factory -from .utils import * -from .widget_css import WidgetCSS +from kivyblocks.utils import * +from kivyblocks.widget_css import WidgetCSS class ScrollPanel(WidgetCSS, ScrollView): orientation = OptionProperty('vertical', \ diff --git a/kivyblocks/tab.py b/kivyblocks/tab.py index 46f0054..d31520b 100755 --- a/kivyblocks/tab.py +++ b/kivyblocks/tab.py @@ -24,8 +24,8 @@ from appPublic.uniqueID import getID from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem from kivy.clock import Clock from kivy.factory import Factory -from .utils import SUPER -from .widget_css import WidgetCSS +from kivyblocks.utils import SUPER +from kivyblocks.widget_css import WidgetCSS class TabsPanel(WidgetCSS, TabbedPanel): def __init__(self, tabs=[], **options): @@ -33,26 +33,44 @@ class TabsPanel(WidgetCSS, TabbedPanel): SUPER(TabsPanel, self, options) # TabbedPanel.__init__(self,**options) # BGColorBehavior.__init__(self) + self.old_content = None + self.def_tab = None + self.register_event_type('on_content_show') + self.register_event_type('on_content_hide') Clock.schedule_once(self.add_tabs,0) - def newname(self): - return getID() - - def add_tab(self,name,text,desc): - def add(o,w): - if not hasattr(w,'widget_id'): - w.widget_id = name - self.add_widget(TabbedPanelItem(text=text,content=w)) + def add_tab(self,desc): + if not desc.get('name'): + desc['name'] = getID() blocks = Factory.Blocks() - blocks.bind(on_built=add) - blocks.widgetBuild(desc) + w = blocks.widgetBuild(desc['content']) + if w is None: + return + if not hasattr(w,'widget_id'): + w.widget_id = desc['name'] + item = TabbedPanelItem(text=desc['text'],content=w) + if desc.get('isdefault', False): + self.def_tab = item + self.add_widget(item) def add_tabs(self,*args): for d in self.tabs_list: - name = d.get('name',self.newname()) - text = d['text'] - desc = d['content'] - self.add_tab(name,text,desc) + self.add_tab(d) + if self.def_tab: + self.switch_to(self.def_tab) + self.bind(current_tab = self.content_changed) + + def content_changed(self, o, v=None): + if self.old_content: + self.dispatch('on_content_hide', self.old_content) + self.old_content = self.current_tab.content + self.dispatch('on_content_show', self.old_content) + + def on_content_show(self, *args): + print('content show', args) + + def on_content_hide(self, *args): + print('content hide', args) if __name__ == '__main__': from kivy.app import App diff --git a/kivyblocks/toolbar.py b/kivyblocks/toolbar.py index 1e248d5..389d4f9 100755 --- a/kivyblocks/toolbar.py +++ b/kivyblocks/toolbar.py @@ -221,6 +221,9 @@ class ToolPage(Box): self.orientation = 'vertical' else: self.orientation = 'horizontal' + self.current_sub_content = None + self.register_event_type('on_content_show') + self.register_event_type('on_content_hide') if not self.toolbar_size: self.toolbar_size = self.img_size + self.text_size + 0.3 self.content_widgets = {} @@ -259,6 +262,7 @@ class ToolPage(Box): def init(self): self.content_w = BoxLayout() + self.curr_sub_content = None self.content_w.widget_id = 'content' self.toolbar_w = Toolbar(**self.toolbar) if self.tool_at in ['top','left']: @@ -300,24 +304,37 @@ class ToolPage(Box): refresh = v.get('refresh', False) # self.print_all() + if self.current_sub_content: + self.dispatch('on_content_hide', self.current_sub_content) + w = self.content_widgets.get(name) self.content_w.clear_widgets() if w and not refresh: self.content_w.add_widget(w) - return - url = v.get('url') - if url: - w = self.build_widget(url) - if w: - self.content_widgets[name] = w - self.content_w.add_widget(w) - return - rfname = v.get('rfname') - if rfname: - rf = RegisterFunction() - f = rf.get(rfname) - if f: - r = f() - if isinstance(r,Widget): - self.content_w.add_widget(r) + else: + w = None + url = v.get('url') + if url: + w = self.build_widget(url) + if w: + self.content_widgets[name] = w + self.content_w.add_widget(w) + else: + rfname = v.get('rfname') + if rfname: + rf = RegisterFunction() + f = rf.get(rfname) + if f: + w = f() + if isinstance(w,Widget): + self.content_widgets[name] = w + self.content_w.add_widget(w) + if w: + self.current_sub_content = w + self.dispatch('on_content_show', w) + def on_content_show(self, content): + pass + + def on_content_hide(self, content): + pass diff --git a/kivyblocks/tree.py b/kivyblocks/tree.py index c185943..cd7f061 100755 --- a/kivyblocks/tree.py +++ b/kivyblocks/tree.py @@ -158,12 +158,28 @@ class TreeNode(BoxLayout): self.node_box1.width = width for n in self.nodes: n.setMinWidth(width) + def build_content(self): + self.buildContent() + x = self.content + cf = self.treeObj.checkField + if cf: + self.content = HBox(size_hint_y=None, + height=self.treeObj.rowHeight) + self.checkbox_w = CheckBox(active=self.data.get(cf, False) + self.content.add_widget(self.checkbox_w) + self.content.add_widget(x) + self.checkbox_w.bind(active=self.on_checkbox_active) + def on_checkbox_active(self, o, v): + cf = self.treeObj.checkField + self.data[cf] = v + self.treeObj.node_checkbox_changed(self) + def buildContent(self): pass def addContent(self): - self.buildContent() + self.build_content() self.node_box.add_widget(self.content) self.node_box.height = self.treeObj.rowheight self.node_box.width = self.trigger.width + \ @@ -304,8 +320,7 @@ tree options "single_expand", "select_leaf_only", "bgcolor", - "checkbox", - "multplecheck", + "checkField", "idField", "textFiled", "data" # array of {children:{},...} @@ -313,7 +328,7 @@ tree options """ class Tree(WidgetCSS, ScrollWidget): data = ListProperty([]) - def __init__(self, + def __init__(self,nodeKlass=TreeNode, url=None, params={}, single_expand=False, @@ -322,13 +337,13 @@ class Tree(WidgetCSS, ScrollWidget): normal_css="default", row_height=2, selected_css="selected", - checkbox=False, - multiplecheck=False, + checkField=None, idField='id', textField='text', data=None, **options): self.url = url + self.nodeKlass = nodeKlass self.params = params self.data = data self.single_expand=single_expand @@ -338,8 +353,7 @@ class Tree(WidgetCSS, ScrollWidget): self.bgcolor = bgcolor self.normal_css = normal_css self.selected_css = selected_css - self.checkbox = checkbox - self.multiplecheck = multiplecheck + self.checkFiled = checkField self.idField = idField self.textField = textField print('options=',options) @@ -351,6 +365,10 @@ class Tree(WidgetCSS, ScrollWidget): self.buildTree() self.bind(size=self.onSize,pos=self.onSize) self.register_event_type('on_press') + self.register_event_type('on_checked') + + def node_checkbox_changed(self, node): + self.dispatch('on_checked', node.data) def on_press(self,*larg): pass diff --git a/kivyblocks/utils.py b/kivyblocks/utils.py index b6e932f..608200b 100755 --- a/kivyblocks/utils.py +++ b/kivyblocks/utils.py @@ -194,8 +194,11 @@ def getWidgetById(w,id): return w.ids.get(id) def CSize(x,y=None,name=None): - ks = KivySizes() - return ks.CSize(x,y=y,name=name) + app = App.get_running_app() + r = app.font_size + if not y: + return r * x + return r * x, r * y def screenSize(): ks = KivySizes() diff --git a/kivyblocks/widgetExt/tree.py b/kivyblocks/widgetExt/tree.py deleted file mode 100755 index 44f3a81..0000000 --- a/kivyblocks/widgetExt/tree.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -tree description json format: -{ - "url":a http(s) url for get data, - "checkbox":boolean, show a checkbox before text - "data":if undefined url,use data to construct the tree - "params":parameters attached to the http(s) request via url - "headers":headers need to set for the http request via url - "height":widget 's height - "width":widget's width -} - -data structure : -{ - "id":identified field, - "widgettype":widget type, - ...other data... - "__children__":[ - ] -} -""" -from .scrollwidget import ScrollWidget -from kivy.uix.treeview import TreeView -