This commit is contained in:
yumoqing 2022-11-13 08:55:49 +08:00
parent ebfbfb115f
commit 1352d23245
12 changed files with 635 additions and 237 deletions

View File

@ -33,6 +33,7 @@ from .orientationlayout import OrientationLayout
from .threadcall import HttpClient from .threadcall import HttpClient
from .register import * from .register import *
from .script import Script, set_script_env from .script import Script, set_script_env
from .mixin import filter_mixin, get_mixins, mixin_behaviors
class WidgetNotFoundById(Exception): class WidgetNotFoundById(Exception):
def __init__(self, id): def __init__(self, id):
@ -237,18 +238,22 @@ class Blocks(EventDispatcher):
return self.dictValueExpr(obj,localnamespace) return self.dictValueExpr(obj,localnamespace)
return obj return obj
def w_build(self,desc) -> Widget: def w_build(self, desc:dict) -> Widget:
widgetClass = desc.get('widgettype',None) widgetClass = desc.get('widgettype',None)
if not widgetClass: if not widgetClass:
Logger.info("Block: w_build(), desc invalid", desc) Logger.info("Block: w_build(), desc invalid", desc)
raise Exception(desc) raise Exception(desc)
widgetClass = desc['widgettype'] widgetClass = desc['widgettype']
opts = self.valueExpr(desc.get('options',{}).copy()) opts_org = self.valueExpr(desc.get('options',{}).copy())
opts = filter_mixin(opts_org)
bopts = get_mixins(opts_org)
widget = None widget = None
try: try:
klass = Factory.get(widgetClass) klass = Factory.get(widgetClass)
widget = klass(**opts) widget = klass(**opts)
mixin_behaviors(widget, bopts)
except Exception as e: except Exception as e:
print('Error:',widgetClass,'contructon error') print('Error:',widgetClass,'contructon error')
print_exc() print_exc()

27
kivyblocks/mixin.py Normal file
View File

@ -0,0 +1,27 @@
from kivy.factory import Factory
_mix_ins = {}
def filter_mixin(kwargs):
return {k:v for k, v in kwargs.items() if not k.endswith('behavior')}
def get_mixins(kwargs):
return {k:v for k, v in kwargs.items() if k.endswith('behavior')}
def register_mixin(name, klass):
_mix_ins[name] = klass
def mix_other(inst, other):
for k,v in other.__dict__.items():
setattr(inst, k, v)
def mixin_behaviors(inst, kwargs):
behaviors_kw = get_mixins(kwargs)
for name, dic in behaviors_kw.items():
klass = _mix_ins.get(name)
if klass:
other = Factory.Blocks().widgetBuild(dic)
if other:
mix_other(inst, other)

118
kivyblocks/modalbehavior.py Normal file
View File

@ -0,0 +1,118 @@
from kivy.properties import DictProperty, BooleanProperty, \
StringProperty, OptionProperty, \
NumericProperty
from mixin import register_mixin
class ModalBehavior(object):
auto_open = BooleanProperty(True)
auto_dismiss = BooleanProperty(True)
target = StringProperty(None)
show_time = NumericProperty(0)
position = OptionProperty('cc',options=['tl', 'tc', 'tr',
'cl', 'cc', 'cr',
'bl', 'bc', 'br'])
def __init__(self, **kw):
self.time_task = None
self._target = None
super(Modal, self).__init__(**kw)
self.set_size_position()
self._target.bind(size=self.set_size_position)
self.register_event_type('on_open')
self.register_event_type('on_pre_open')
self.register_event_type('on_pre_dismiss')
self.register_event_type('on_dismiss')
self.bind(on_touch_down=self.on_touchdown)
if self.auto_open:
self.open()
def on_touchdown(self, o, touch):
if not self.collide_point(touch.x, touch.y):
if self.auto_dismiss:
self.dispatch('on_pre_dismiss')
self.dismiss()
return True
def on_target(self, *args):
self._target = None
self.set_target(self)
def set_target(self):
if self._target is None:
if self.target is None:
w = Window
else:
w = Factory.Blocks.getWidgetById(self.target, from_target=self)
if w is None:
w = Window
self._target = w
def set_size_position(self, *args):
self.set_target()
if self.size_hint_x:
self.width = self.size_hint_x * self._target.width
if self.size_hint_y:
self.height = self.size_hint_y * self._target.height
self.set_modal_position()
def set_modal_position(self):
self.set_target()
xn = self.position[1]
yn = self.position[0]
x, y = 0, 0
if xn == 'c':
x = (self._target.width - self.width) / 2
elif xn == 'r':
x = self._target.width - self.width
if x < 0:
x = 0
if yn == 'c':
y = (self._target.height - self.height) / 2
elif yn == 't':
y = self._target.height - self.height
if y < 0:
y = 0
if self._target == Window:
self.pos = x, y
else:
self.pos = self._target.pos[0] + x, self._target.pos[1] + y
def open(self):
if self.time_task is not None:
self.time_task.cancel()
self.time_task = None
if self.show_time > 0:
self.time_task = \
Clock.schedule_once(self.dismiss, self.show_time)
if self.parent:
self.parent.remove_widget(self)
self.dispatch('on_pre_open')
Window.add_widget(self)
self.dispatch('on_open')
if self._target != Window:
self._target.disabled = True
def dismiss(self, *args):
if self.time_task:
self.time_task.cancel()
self.time_task = None
self.dispatch('on_pre_dismiss')
self.dispatch('on_dismiss')
Window.remove_widget(self)
if self._target != Window:
self._target.enabled = False
def on_open(self, *args):
pass
def on_dismiss(self, *args):
pass
def on_pre_open(self, *args):
pass
def on_pre_dismiss(self, *args):
pass
register_mixin('modalbehavior', ModalBehavior)

View File

@ -11,6 +11,7 @@ from kivy.uix.boxlayout import BoxLayout
from kivy.uix.modalview import ModalView from kivy.uix.modalview import ModalView
from kivy.uix.image import AsyncImage from kivy.uix.image import AsyncImage
from appPublic.dictObject import DictObject from appPublic.dictObject import DictObject
from .mixin import filter_mixin, get_mixins, mixin_behaviors
from .kivysize import KivySizes from .kivysize import KivySizes
@ -40,17 +41,22 @@ def kwarg_pop(obj, kw):
setattr(obj, k, kw.pop(k)) setattr(obj, k, kw.pop(k))
def SUPER(klass, obj, kw): def SUPER(klass, obj, kw):
mixins_kw = get_mixins(kw)
kw = filter_mixin(kw)
keys = [ k for k in kw.keys() ] keys = [ k for k in kw.keys() ]
dic = { k:kw.pop(k) for k in keys if hasattr(obj, k) } dic = { k:kw.pop(k) for k in keys if hasattr(obj, k) }
super(klass, obj).__init__(**kw) super(klass, obj).__init__(**kw)
for k,v in dic.items(): for k,v in dic.items():
try: try:
setattr(obj, k, v) if v is not None:
setattr(obj, k, v)
except Exception as e: except Exception as e:
print(f'obj={obj}, setattr(obj, "{k}","{v}") error') print(f'obj={obj}, setattr(obj, "{k}","{v}") error')
print_exc() print_exc()
raise e raise e
mixin_behaviors(obj, mixins_kw)
def blockImage(name): def blockImage(name):
p = os.path.dirname(os.path.abspath(__file__)) p = os.path.dirname(os.path.abspath(__file__))
return os.path.join(p,'imgs',name) return os.path.join(p,'imgs',name)

View File

@ -0,0 +1,30 @@
{
"id":"mainctrl",
"widgettype":"PagePanel",
"options":{
"bar_autohide":true,
"i18n":true,
"bar_size":2,
"bar_at":"top",
"csscls":"mainctrl",
"singlepage":true,
"left_menu":{
"widgettype":"urlwidget",
"options":{
"params":{
"userid":"testuser"
},
"url":"{{entire_url('mainmenu.ui')}}"
}
}
},
"subwidgets":[
{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('t.ui')}}"
}
}
]
}

View File

@ -0,0 +1,106 @@
{
"id":"m3u_modal",
"widgettype":"Modal",
"options":{
"auto_open":true,
"position":"tl",
"csscls":"modal_panel",
"size_hint":[0.7,0.7],
"content":{
"id":"modal_content",
"widgettype":"VBox",
"options":{
},
"subwidgets":[
{
"id":"m3u_form",
"widgettype":"Form",
"options":{
"cols":1,
"labelwidth":0.3,
"inputheight":2,
"fields":[
{
"name":"url",
"label":"Url",
"required":true,
"datatype":"str"
},
{
"name":"name",
"label":"Name",
"datatype":"str",
"required":true,
"uitype":"str"
}
]
}
},
{
"widgettype":"HBox",
"options":{},
"subwidgets":[
{
"widgettype":"Text",
"options":{
"otext":"total channels",
"size_hint_x":null,
"width":"py::CSize(14)",
"wrap":true,
"halign":"right"
}
},
{
"id":"tc_w",
"widgettype":"Text",
"options":{
"text":"",
"size_hint_x":null,
"width":"py::CSize(14)",
"wrap":true,
"halign":"left"
}
}
]
},
{
"widgettype":"HBox",
"options":{},
"subwidgets":[
{
"widgettype":"Text",
"options":{
"otext":"good channels",
"size_hint_x":null,
"width":"py::CSize(14)",
"wrap":true,
"halign":"right"
}
},
{
"id":"gc_w",
"widgettype":"Text",
"options":{
"text":"",
"size_hint_x":null,
"width":"py::CSize(14)",
"wrap":true,
"halign":"left"
}
}
]
}
]
}
},
"binds":[
{
"wid":"m3u_form",
"event":"on_submit",
"actiontype":"script",
"datawidget":"m3u_form",
"target":"app",
"script":"self.open_m3u(**kwargs)"
}
]
}

View File

@ -0,0 +1,64 @@
{
"widgettype":"Modal",
"options":{
"auto_open":true,
"position":"tl",
"csscls":"modal_panel",
"size_hint":[0.7,0.7],
"content":{
"id":"url_form",
"widgettype":"Form",
"options":{
"cols":1,
"labelwidth":0.3,
"inputheight":2,
"fields":[
{
"name":"url",
"label":"Url",
"required":true,
"datatype":"str"
},
{
"name":"urltype",
"label":"urltype",
"datatype":"str",
"uitype":"code",
"uiparams":{
"valueField":"ut",
"textField":"ut",
"data":[
{
"ut":"video"
},
{
"ut":"m3u"
},
{
"ut":"ebook"
}
]
}
}
]
}
}
},
"binds":[
{
"wid":"url_form",
"event":"on_submit",
"actiontype":"script",
"target":"self",
"script":"self.dismiss()"
},
{
"wid":"url_form",
"event":"on_submit",
"actiontype":"script",
"datawidget":"url_form",
"target":"app",
"script":"self.open_url(**kwargs)"
}
]
}

View File

@ -0,0 +1,20 @@
{
"widgettype":"Menu",
"options":{
"single_expand":true,
"idField":"id",
"font_size_c":1,
"node_height":2.5,
"textField":"label",
"bgcolor":[0.2,0.2,0.2,1],
"target":"root.mainctrl",
"data":[
{
"id":"setting",
"label":"Setting",
"icon":"{{entire_url('/imgs/setting.png')}}",
"url":"{{entire_url('t.ui')}}"
}
]
}
}

View File

@ -0,0 +1,5 @@
{
"widgettype":"PyInterpreter",
"options":{
}
}

View File

@ -0,0 +1,4 @@
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=898222,RESOLUTION=1047x576,CODECS="avc1.77.30,mp4a.40.2"
chunklist_w891111828.m3u8

6
test/script/scripts/t.ui Normal file
View File

@ -0,0 +1,6 @@
{
"widgettype":"Text",
"options":{
"text":"To Be Implements"
}
}

View File

@ -0,0 +1,7 @@
{
"widgettype":"Text",
"options":{
"text":"Welcome to Kivyblocks",
"i18n":true
}
}