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 .register import *
from .script import Script, set_script_env
from .mixin import filter_mixin, get_mixins, mixin_behaviors
class WidgetNotFoundById(Exception):
def __init__(self, id):
@ -237,18 +238,22 @@ class Blocks(EventDispatcher):
return self.dictValueExpr(obj,localnamespace)
return obj
def w_build(self,desc) -> Widget:
def w_build(self, desc:dict) -> Widget:
widgetClass = desc.get('widgettype',None)
if not widgetClass:
Logger.info("Block: w_build(), desc invalid", desc)
raise Exception(desc)
widgetClass = desc['widgettype']
opts = self.valueExpr(desc.get('options',{}).copy())
opts_org = self.valueExpr(desc.get('options',{}).copy())
opts = filter_mixin(opts_org)
bopts = get_mixins(opts_org)
widget = None
try:
klass = Factory.get(widgetClass)
widget = klass(**opts)
mixin_behaviors(widget, bopts)
except Exception as e:
print('Error:',widgetClass,'contructon error')
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.image import AsyncImage
from appPublic.dictObject import DictObject
from .mixin import filter_mixin, get_mixins, mixin_behaviors
from .kivysize import KivySizes
@ -40,17 +41,22 @@ def kwarg_pop(obj, kw):
setattr(obj, k, kw.pop(k))
def SUPER(klass, obj, kw):
mixins_kw = get_mixins(kw)
kw = filter_mixin(kw)
keys = [ k for k in kw.keys() ]
dic = { k:kw.pop(k) for k in keys if hasattr(obj, k) }
super(klass, obj).__init__(**kw)
for k,v in dic.items():
try:
if v is not None:
setattr(obj, k, v)
except Exception as e:
print(f'obj={obj}, setattr(obj, "{k}","{v}") error')
print_exc()
raise e
mixin_behaviors(obj, mixins_kw)
def blockImage(name):
p = os.path.dirname(os.path.abspath(__file__))
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
}
}