diff --git a/kivyblocks/.toolbar.py.swp b/kivyblocks/.toolbar.py.swp deleted file mode 100644 index cbcb8e3..0000000 Binary files a/kivyblocks/.toolbar.py.swp and /dev/null differ diff --git a/kivyblocks/clickable.py b/kivyblocks/clickable.py index 0385ed3..a31b263 100644 --- a/kivyblocks/clickable.py +++ b/kivyblocks/clickable.py @@ -10,13 +10,14 @@ from kivy.properties import NumericProperty, DictProperty, \ from kivyblocks.ready import WidgetReady from kivyblocks.bgcolorbehavior import BGColorBehavior -from kivyblocks.utils import CSize, SUPER +from kivyblocks.utils import CSize, SUPER, blockImage from kivyblocks.baseWidget import Box, Text from kivyblocks.widget_css import WidgetCSS class TinyText(Text): def __init__(self, **kw): SUPER(TinyText, self, kw) + self.size_hint = (None, None) self.on_texture_size(self, (1,1)) def on_texture_size(self, o, ts): @@ -28,6 +29,7 @@ class ClickableBox(TouchRippleButtonBehavior, Box): border_width=1, **kw): SUPER(ClickableBox, self, kw) + self.size_hint = [None, None] self.border_width = border_width # self.bind(minimum_height=self.setter('height')) # self.bind(minimum_width=self.setter('width')) @@ -49,7 +51,6 @@ class ClickableText(ClickableBox): text = StringProperty(' ') fontsize = NumericProperty(1) def __init__(self, **kw): - print('ClickableText begin inited') self.txt_w = None SUPER(ClickableText, self, kw) self.txt_w = TinyText(otext=self.text, @@ -72,7 +73,6 @@ class ClickableIconText(ClickableText): def __init__(self, **kw): self.img_w = None SUPER(ClickableIconText, self, kw) - print('ClickableIconText inited') def on_source(self, o, source): if self.img_w: @@ -111,7 +111,6 @@ class ToggleText(ClickableText): def on_select_state(self, o, f): self.csscls = self.css_on if f else self.css_off - print(f'using {self.csscls}') class ToggleIconText(ToggleText): """ @@ -149,8 +148,6 @@ class ClickableImage(ClickableBox): def __init__(self, **kw): self.img_w = None SUPER(ClickableImage, self, kw) - # self.img_w = AsyncImage(source=self.source, **self.img_kw) - # self.add_widget(self.img_w) def on_source(self, o, source): if self.source is None: @@ -200,25 +197,69 @@ class ToggleImage(ClickableImage): else: self.source = self.source_off -class CheckBox(ToggleImage): +class SingleCheckBox(ClickableBox): otext = StringProperty(None) + select_state = BooleanProperty(False) def __init__(self, **kw): - SUPER(CheckBox, self, kw) + self.old_state = None + self.register_event_type('on_changed') + SUPER(SingleCheckBox, self, kw) self.source_on = blockImage('checkbox-on.png') - self.source_off = blockImage('ceckbox-off.png') + self.source_off = blockImage('checkbox-off.png') + self.select(False) + self.create_img_w() if self.otext: self.txt_w = TinyText(otext=self.otext, i18n=True) + self.txt_w.bind(size=self.reset_size) self.add_widget(self.txt_w) + self.reset_size(self, None) + + def on_changed(self, o=None): + pass + + def on_press(self, o=None): + self.toggle() + + def select(self, f): + self.select_state = f + + def change_img_source(self): + self.img_w.source = self.source_on if self.select_state else \ + self.source_off + + def toggle(self): + self.select_state = False if self.select_state else True + + def state(self): + return self.select_state + + def on_select_state(self, o, f): + self.change_img_source() + self.dispatch('on_changed', self.select_state) def getValue(self): return self.state() def setValue(self, v): - self.select_state = False if v else True + self.select(True if v else False) + + def create_img_w(self): + if self.select_state: + self.img_w = AsyncImage(source=self.source_on, + size_hint=(None, None), + size=CSize(1, 1) + ) + else: + self.img_w = AsyncImage(source=self.source_off, + size_hint=(None, None), + size=CSize(1, 1) + ) + self.add_widget(self.img_w) + self.img_w.bind(size=self.reset_size) r = Factory.register r('TinyText', TinyText) -r('CheckBox', CheckBox) +r('SingleCheckBox', SingleCheckBox) r('ClickableBox', ClickableBox) r('ClickableText',ClickableText) r('ClickableIconText',ClickableIconText) diff --git a/kivyblocks/multi_select.py b/kivyblocks/multi_select.py index fed71a5..21260ec 100644 --- a/kivyblocks/multi_select.py +++ b/kivyblocks/multi_select.py @@ -1,67 +1,149 @@ +from kivy.clock import Clock +from kivy.factory import Factory +from kivy.uix.stacklayout import StackLayout from kivy.properties import BooleanProperty, StringProperty,\ - ListProperty, DictProperty + ListProperty, DictProperty, OptionProperty, \ + NumericProperty + from kivyblocks.baseWidget import VBox from kivyblocks.clickable import ToggleText, ToggleImage -from kivyblocks.typewriterbox import TypeWriterBox -from kivy.factory import Factory +from kivyblocks.utils import SUPER, CSize +from kivyblocks.widget_css import WidgetCSS +from kivyblocks.ready import WidgetReady -class MultiSelect(TypeWriterBox): +class MultiSelect(WidgetCSS, WidgetReady, StackLayout): + """ + { + "widgettype":"MultiSelect", + "options":{ + "valueField" + "textField" + "items" + "item_css" + "item_selected_css" + "all_position" + "need_others" + "default_selected" + } + } + """ + valueField = StringProperty('value') + textField = StringProperty('text') + x_spacing_c = NumericProperty(1) + y_spacing_c = NumericProperty(0.5) items = ListProperty([]) - item_css = DictProperty({}) - item_selected_css = DictProperty({}) - all_button_position = StringProperty(None) - default_selected=StringProperty([]) - + item_css = StringProperty({}) + item_selected_css = StringProperty({}) + all_position = OptionProperty(None, \ + options=[None, 'begin', 'end']) + need_others = BooleanProperty(False) + default_selected=ListProperty([]) + result = ListProperty([]) def __init__(self, **kw): - super(MultiSelect, self).__init__(**kw) + SUPER(MultiSelect, self, kw) + self.spacing = CSize(self.x_spacing_c, self.y_spacing_c) + self.orientation = 'lr-tb' + self.build_all_items() + self.old_result = [] + self.register_event_type('on_changed') self.button_dic = {} self.value_b = {} + self.buildWidgets() + + def build_all_items(self): + items = [i for i in self.items ] + if self.need_others: + dic = { + self.valueField:'_other_', + self.textField:'others' + } + items.append(dic) - def buildItem(self, dic): + if self.all_position is not None: + dic = { + self.textField:'all', + self.valueField:'_all_', + } + if self.all_position == 'begin': + items.insert(0, dic) + else: + items.append(dic) + self.all_items = items + + def on_changed(self, d): + pass + + def build_item_widget(self, dic): keys = dic.keys() ToggleText = Factory.ToggleText ToggleImage = Factory.ToggleImage - if 'text' in keys and 'value' in keys: - desc = dic.copy() - desc['off_css'] = self.item_css - desc['on_css'] = self.item_selected_css + ToggleIconText = Factory.ToggleIconText + if self.textField in keys and 'source_on' in keys and \ + 'source_off' in keys: + desc['text'] = dic[self.textField] + desc['css_off'] = self.item_css + desc['css_on'] = self.item_selected_css + desc["source_on"] = dic['source_on'], + desc["source_off"] = dic['source_off'] + print('ToggleIconText', desc) + return ToggleIconText(**desc) + + if self.textField in keys: + desc = {} + desc['text'] = dic[self.textField] + desc['css_off'] = self.item_css + desc['css_on'] = self.item_selected_css + print('ToggleText', desc) return ToggleText(**desc) - if 'source' in keys and 'on_source' in keys: - return ToggleImage(**dic) - - def on_items(self, o, items): - self.clear_widgets() - if all_button_position is not None: - dic = { - 'text':'all', - 'value':'_all_', + if 'source_on' in keys and 'source_off' in keys: + desc = { + "source_on":dic['source_on'], + "source_off":dic['source_off'] } - if all_button_position == 'begin': - items.insert(dic,0) - else: - items.append(dic) - for item in items: - b = self.buildItem(item) + print('ToggleImage', desc) + return ToggleImage(**desc) + + def buildWidgets(self): + self.clear_widgets() + for item in self.all_items: + b = self.build_item_widget(item) b.bind(on_press=self.button_pressed) - self.button_dic.update({b:dic}) - self.value_b.update({dic['value']:b}) + self.button_dic.update({b:item}) + self.add_widget(b) def button_pressed(self, o): o.toggle() - if self.button_dic[o]['value'] == '_all_' and o.state(): - for b, dic in self.button_dic.copy().items(): + if self.button_dic[o][self.valueField] == '_all_' and o.state(): + for b, dic in self.button_dic.items(): if b == o: continue b.select(False) + if self.button_dic[o][self.valueField] == '_other_' and o.state(): + for b, dic in self.button_dic.items(): + if b == o: + continue + b.select(False) + if self.button_dic[o][self.valueField] not in ['_all_', '_other_'] \ + and o.state(): + for b, dic in self.button_dic.items(): + if dic[self.valueField] in ['_all_', '_other_']: + b.select(False) + Clock.schedule_once(self.set_result, 0.1) + + def set_result(self, t=None): + self.result = [ d[self.valueField] for b, d in \ + self.button_dic.items() if b.state() ] + if self.result != self.old_result: + self.dispatch('on_changed', self.result) + self.old_result = self.result def getValue(self): - selected_buttons = [ b for b in self.button_dic.item() \ - if b.state() ] - r = [ dic['value'] for b, dic in self.button_dic.items() \ - if b in selected_buttons ] - return r + self.set_result() + return self.result def setValue(self, value): + self.result = value + self.old_result = value for v in value: b = self.value_b[v] b.select(True) diff --git a/kivyblocks/ready.py b/kivyblocks/ready.py index 77c0147..ad0dd18 100644 --- a/kivyblocks/ready.py +++ b/kivyblocks/ready.py @@ -1,6 +1,8 @@ + from traceback import print_exc from contextlib import contextmanager +from kivy.graphics import Fbo, Color, Rectangle from kivy.core.window import Window from kivy.clock import Clock from kivy.utils import platform diff --git a/kivyblocks/tree.py b/kivyblocks/tree.py index 4b78dc6..b20bbf3 100644 --- a/kivyblocks/tree.py +++ b/kivyblocks/tree.py @@ -19,7 +19,7 @@ from appPublic.registerfunction import getRegisterFunctionByName from .baseWidget import PressableLabel, Text, HBox, VBox from .color_definitions import getColors from .widget_css import WidgetCSS -from .utils import alert,absurl +from .utils import alert,absurl, SUPER from .toggleitems import PressableBox from .threadcall import HttpClient @@ -38,12 +38,11 @@ class EmptyBox(Label): class NodeTrigger(ButtonBehavior, EmptyBox): open_status = BooleanProperty(False) color = ListProperty([1,0,0,1]) - def __init__(self, color=[1,0,0,1],**kw): - # super(NodeTrigger, self).__init__(**kw) - EmptyBox.__init__(self, **kw) - ButtonBehavior.__init__(self) + def __init__(self, **kw): + self.open_points = None + self.close_points = None + SUPER(NodeTrigger, self, kw) self.countPoints() - self.color = color self.bind(size=self.onSize,pos=self.onSize) def countPoints(self): @@ -65,7 +64,6 @@ class NodeTrigger(ButtonBehavior, EmptyBox): def on_press(self, *args): self.open_status = False if self.open_status else True - # self.showOpenStatus() def on_open_status(self, *largs): self.showOpenStatus() @@ -74,6 +72,8 @@ class NodeTrigger(ButtonBehavior, EmptyBox): self.showOpenStatus() def showOpenStatus(self): + if self.close_points is None: + return points = self.close_points if self.open_status: points = self.open_points diff --git a/kivyblocks/typewriterbox.py b/kivyblocks/typewriterbox.py deleted file mode 100644 index 8ccc43b..0000000 --- a/kivyblocks/typewriterbox.py +++ /dev/null @@ -1,50 +0,0 @@ -from kivy.uix.boxlayout import BoxLayout -from kivy.uix.widget import Widget -from kivy.factory import Factory -from kivyblocks.widget_css import WidgetCSS -from kivyblocks.ready import WidgetReady - -class TypeWriterBox(WidgetCSS, WidgetReady, Widget): - def __init__(self, **kw): - super(TypeWriterBox, self).__init__(**kw) - self.size_hint_y = None - self.sub_widgets = [] - self.curbox = self.add_vertical_box() - self.bind(minimum_height=self.setter('height')) - - def on_width(self, o, size): - self.relocate() - - def add_widget(self, w, *args): - width = 0 - for c in self.curbox.children: - width += c.width - if width + c.width > self.width: - self.curbox = self.add_vertical_box() - self.curbox.add_widget(w, *args) - self.sub_widgets.append(w) - - def del_widget(self, w): - self.sub_widgets = [i for i in self.sub_widgets.copy() if i !=w ] - self.relocate() - - def add_veritcal_box(self): - box = BoxLayout(orientation='vertical', size_hint_y=None) - box.bind(minimum_height = box.setter('height')) - super(TypeWriterBox, self).add_widget(box) - return box - - def relocate(self): - with self.fboContext(): - for b in self.children: - b.clear_widgets() - width = 0 - self.clear_widgets() - self.curbox = add_vertical_box() - for w in self.sub_widgets: - if width + w.width > self.width: - self.curbox = self.add_vertical_box() - width = 0 - self.curbox.add_widget(w) - -Factory.register('TypeWriterBox', TypeWriterBox) diff --git a/kivyblocks/utils.py b/kivyblocks/utils.py index 637f238..7583f7f 100644 --- a/kivyblocks/utils.py +++ b/kivyblocks/utils.py @@ -1,5 +1,6 @@ import os from traceback import print_exc +from traceback import print_exc from kivy.app import App from appPublic.jsonConfig import getConfig from kivy.uix.popup import Popup @@ -42,7 +43,12 @@ def SUPER(klass, obj, kw): dic = { k:kw.pop(k) for k in keys if hasattr(obj, k) } super(klass, obj).__init__(**kw) for k,v in dic.items(): - setattr(obj, k, v) + try: + setattr(obj, k, v) + except Exception as e: + print(f'obj={obj}, setattr(obj, "{k}","{v}") error') + print_exc() + raise e def blockImage(name): p = os.path.dirname(os.path.abspath(__file__)) diff --git a/kivyblocks/widgetExt/inputext.py b/kivyblocks/widgetExt/inputext.py index a65de22..e41d8ea 100755 --- a/kivyblocks/widgetExt/inputext.py +++ b/kivyblocks/widgetExt/inputext.py @@ -77,7 +77,6 @@ class StrInput(TextInput): def checkChange(self,o,v=None): v = self.getValue() - print('StrInput():v=', v) if v != self.old_value: self.old_value = v self.dispatch('on_changed',v) @@ -89,6 +88,8 @@ class StrInput(TextInput): if v is None: v = '' self.text = str(v) + self.old_value = self.text + class Password(StrInput): def __init__(self, **kw):