master
yumoqing 2022-11-14 17:07:42 +08:00
parent 1352d23245
commit 9b6084e15d
11 changed files with 335 additions and 230 deletions

View File

@ -1,67 +1,69 @@
from kivy.logger import Logger
from kivy.graphics import Color, Rectangle, RoundedRectangle
from kivy.properties import ListProperty, NumericProperty
from .color_definitions import getColors
_logcnt = 0
class BGColorBehavior(object):
color_level=NumericProperty(0)
radius = ListProperty([])
def __init__(self, **kwargs):
self.bg_ready = False
self.bgcolor = []
self.fgcolor = []
fg,bg= getColors(self.color_level)
self.fgcolor = fg
self.bgcolor = bg
self.normal_bgcolor = bg
self.normal_fgcolor = fg
fg,bg= getColors(self.color_level,selected=True)
self.selected_bgcolor = bg
self.selected_fgcolor = fg
# self.on_bgcolor()
self.bind(size=self.onSize_bgcolor_behavior,
pos=self.onSize_bgcolor_behavior)
# self.bind(children=self.on_bgcolor)
def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'):
self.on_bgcolor()
else:
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)
if self.radius != []:
self.rect = RoundedRectangle(pos=self.pos,
size=self.size,
radius=self.radius)
else:
self.rect = Rectangle(pos=self.pos,
size=self.size)
else:
print('on_bgcolor():self.canvas is None')
def is_selected(self):
return self.bgcolor == self.selected_bgcolor
def selected(self):
if self.bgcolor == self.selected_bgcolor:
return
self.bgcolor = self.selected_bgcolor
self.color = self.fgcolor = self.selected_fgcolor
self.on_bgcolor()
def unselected(self):
if self.bgcolor == self.normal_bgcolor:
return
self.bgcolor = self.normal_bgcolor
self.color = self.fgcolor = self.normal_fgcolor
self.on_bgcolor()
from kivy.logger import Logger
from kivy.graphics import Color, Rectangle, RoundedRectangle
from kivy.properties import ListProperty, NumericProperty
from .color_definitions import getColors
from .mixin import register_mixin
class BGColorBehavior(object):
color_level=NumericProperty(0)
radius = ListProperty([])
def __init__(self, **kwargs):
self.bg_ready = False
self.bgcolor = []
self.fgcolor = []
fg,bg= getColors(self.color_level)
self.fgcolor = fg
self.bgcolor = bg
self.normal_bgcolor = bg
self.normal_fgcolor = fg
fg,bg= getColors(self.color_level,selected=True)
self.selected_bgcolor = bg
self.selected_fgcolor = fg
# self.on_bgcolor()
self.bind(size=self.onSize_bgcolor_behavior,
pos=self.onSize_bgcolor_behavior)
# self.bind(children=self.on_bgcolor)
def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'):
self.on_bgcolor()
else:
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)
if self.radius != []:
self.rect = RoundedRectangle(pos=self.pos,
size=self.size,
radius=self.radius)
else:
self.rect = Rectangle(pos=self.pos,
size=self.size)
else:
print('on_bgcolor():self.canvas is None')
def is_selected(self):
return self.bgcolor == self.selected_bgcolor
def selected(self):
if self.bgcolor == self.selected_bgcolor:
return
self.bgcolor = self.selected_bgcolor
self.color = self.fgcolor = self.selected_fgcolor
self.on_bgcolor()
def unselected(self):
if self.bgcolor == self.normal_bgcolor:
return
self.bgcolor = self.normal_bgcolor
self.color = self.fgcolor = self.normal_fgcolor
self.on_bgcolor()
register_mixin('BGColorBehavior', BGColorBehavior)

View File

@ -103,6 +103,7 @@ def registerWidget(name:str,widget):
globals()[name] = widget
klass_cnt = 0
class Blocks(EventDispatcher):
def __init__(self):
EventDispatcher.__init__(self)
@ -113,6 +114,47 @@ class Blocks(EventDispatcher):
config = getConfig()
self.script = Script()
def klassBuild(self, klass_name, options):
global klass_cnt
exclude_mixin = filter_mixin(options)
mixins = get_mixins(options)
klasses = [
{
'name':klass_name,
'klass':Factory.get(klass_name),
'opts':exclude_mixin
}
]
if mixins == {}:
return Factory.get(klass_name)(**options)
env = globals().copy()
lenv = {}
for n, d, in mixins.items():
klasses.append({
'name':n,
'klass':Factory.get(n),
'opts':d
})
print('mixins=', mixins)
env['klasses'] = klasses
ids = [ i for i in range(len(klasses))]
ids.reverse()
klasslist = ','.join([f'klasses[{i}]["klass"]' for i in ids])
code = f"""class ClassX{klass_cnt}({klasslist}):
def __init__(self):
for i in range(len(klasses)):
print(klasses[i]['opts'])
klasses[i]['klass'].__init__(self, **klasses[i]['opts'])
x = ClassX{klass_cnt}()
"""
print(code)
exec(code, env, lenv)
w = lenv.get('x', None)
klass_cnt += 1
return w
def set(self, k:str, v):
self.env[k] = v
@ -243,19 +285,17 @@ class Blocks(EventDispatcher):
if not widgetClass:
Logger.info("Block: w_build(), desc invalid", desc)
raise Exception(desc)
widgetClass = desc['widgettype']
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)
"""
widget = self.klassBuild(widgetClass, opts_org)
except Exception as e:
print('Error:',widgetClass,'contructon error')
print('Error:',widgetClass,'construction error')
print_exc()
raise NotExistsObject(widgetClass)
@ -314,7 +354,7 @@ class Blocks(EventDispatcher):
kw = self.valueExpr(sw.copy(),
localnamespace={'self':widget})
w = b.widgetBuild(kw)
if w:
if w and not w.parent:
widget.add_widget(w)
@ -684,7 +724,9 @@ class Blocks(EventDispatcher):
if widgettype is None:
print('Block3: desc must be a dict object not None')
return None
return doit(desc)
w = doit(desc)
print('widgetBuild():w=', w)
return w
@classmethod
def findWidget(self, id:str, from_widget:Widget=None) -> Widget:

View File

@ -1,27 +1,62 @@
from kivy.factory import Factory
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.behaviors import CodeNavigationBehavior
from kivy.uix.behaviors import CompoundSelectionBehavior
from kivy.uix.behaviors import CoverBehavior
from kivy.uix.behaviors import DragBehavior
from kivy.uix.behaviors import EmacsBehavior
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.behaviors import TouchRippleBehavior
from kivy.uix.behaviors import TouchRippleButtonBehavior
from kivy.factory import Factory
_mix_ins = {}
# def on_press(inst, *args):
# pass
# ButtonBehavior.on_press = on_press
def filter_mixin(kwargs):
return {k:v for k, v in kwargs.items() if not k.endswith('behavior')}
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')}
return {k:v for k, v in kwargs.items() if k.endswith('Behavior')}
def register_mixin(name, klass):
_mix_ins[name] = klass
Factory.register(name, klass)
def mix_other(inst, other):
for k,v in other.__dict__.items():
setattr(inst, k, v)
excluded = ['__class__', '__dict__', '__weakref__']
keys = [ k for k in dir(other) if k not in excluded ]
for k in keys:
if not hasattr(inst, k):
setattr(inst, k, getattr(other, k))
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)
mix_other(klass, inst.__class__)
desc = {
"widgettype":name,
"options":dic
}
other = Factory.Blocks().widgetBuild(desc)
if other:
mix_other(inst, other)
register_mixin('ButtonBehavior', ButtonBehavior)
register_mixin('CodeNavigationBehavior', CodeNavigationBehavior)
register_mixin('CompoundSelectionBehavior', CompoundSelectionBehavior)
register_mixin('CoverBehavior', CoverBehavior)
register_mixin('DragBehavior', DragBehavior)
register_mixin('EmacsBehavior', EmacsBehavior)
register_mixin('FocusBehavior', FocusBehavior)
register_mixin('ToggleButtonBehavior', ToggleButtonBehavior)
register_mixin('TouchRippleBehavior', TouchRippleBehavior)
register_mixin('TouchRippleButtonBehavior', TouchRippleButtonBehavior)

View File

@ -1,10 +1,9 @@
from kivy.core.window import Window
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)
@ -14,9 +13,10 @@ class ModalBehavior(object):
'cl', 'cc', 'cr',
'bl', 'bc', 'br'])
def __init__(self, **kw):
for k,v in kw.items():
setattr(self, k,v)
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')
@ -28,11 +28,17 @@ class ModalBehavior(object):
self.open()
def on_touchdown(self, o, touch):
if not self.collide_point(touch.x, touch.y):
print('on_touchdown() called')
if not self.collide_point(touch.x, touch.y):
if self.auto_dismiss:
print('on_touchdown():auto_dismiss()')
self.dispatch('on_pre_dismiss')
self.dismiss()
return True
else:
print('on_touchdown(): not auto_dismiss')
else:
print('on_touchdown():inside modal')
def on_target(self, *args):
self._target = None
@ -77,6 +83,7 @@ class ModalBehavior(object):
self.pos = x, y
else:
self.pos = self._target.pos[0] + x, self._target.pos[1] + y
print("modal",self._target.size, self.position, self.pos, self.size, self.size_hint)
def open(self):
if self.time_task is not None:
@ -94,6 +101,7 @@ class ModalBehavior(object):
self._target.disabled = True
def dismiss(self, *args):
print('dismiss() called')
if self.time_task:
self.time_task.cancel()
self.time_task = None
@ -115,4 +123,3 @@ class ModalBehavior(object):
def on_pre_dismiss(self, *args):
pass
register_mixin('modalbehavior', ModalBehavior)

View File

@ -45,10 +45,12 @@ from .ffpyplayer_video import FFVideo
from .upload import UploadFile
from .pyinterpreter import PyInterpreter
from .circle_progress import CircleProgress
from .modalbehavior import ModalBehavior
r = Factory.register
# if kivy.platform in ['win','linux', 'macosx']:
# r('ScreenWithMic', ScreenWithMic)
r('ModalBehavior', ModalBehavior)
r('CircleProgress', CircleProgress)
r('PyInterpreter', PyInterpreter)
r('UploadFile', UploadFile)

View File

@ -35,10 +35,10 @@ class Script:
self.register('.ui', TemplateHandler)
def url2filepath(self, url):
if url.startswith('file:///'):
url = url[8:]
if url.startswith('file://'):
url = url[7:]
ret = join(self.root, *url.split('/'))
# print('url2filepath():root=', self.root, url, ret)
print('url2filepath():root=', self.root, url, ret)
return ret
def show_info(self, env):

View File

@ -1,118 +1,124 @@
import time
from kivy.logger import Logger
class SwipeBehavior(object):
def __init__(self, **kwargs):
object.__init__(self)
self.swipeenable = True
self.register_event_type('on_context_menu')
self.register_event_type('on_swipe_left')
self.register_event_type('on_swipe_right')
self.register_event_type('on_swipe_up')
self.register_event_type('on_swipe_down')
self.sb_start_point = None
self.sb_end_point = None
self.sb_start_time = None
self.sb_end_time = None
self.threshold=20
self.threshold_time = 0.6
self.bind(on_touch_down=self.on_touchdown)
self.bind(on_touch_move=self.on_touchmove)
self.bind(on_touch_up=self.on_touchup)
def disable_swipe(self):
self.swipeenable = False
def enable_swipe(self):
self.swipeenable = True
def on_context_menu(self):
pass
# Logger.info('SwipeBehavior:on_context_menu fired')
def on_swipe_left(self):
pass
# Logger.info('SwipeBehavior:on_swipe_left fired')
def on_swipe_right(self):
pass
# Logger.info('SwipeBehavior:on_swipe_right fired')
def on_swipe_up(self):
pass
# Logger.info('SwipeBehavior:on_swipe_up fired')
def on_swipe_down(self):
pass
# Logger.info('SwipeBehavior:on_swipe_down fired')
def on_touchdown(self,o, touch):
if touch.is_mouse_scrolling:
# Logger.info('SwipeBehavior:is_mouse_scrolling')
return False
if not self.collide_point(touch.x, touch.y):
return False
# Logger.info('SwipeBehavior:touch_down fired')
if self.swipeenable:
# Logger.info('SwipeBehavior:touch_down fired')
self.sb_start_point = touch.pos
self.sb_start_time = time.time()
def on_touchmove(self,o,touch):
if self.collide_point(*touch.pos) and self.swipeenable:
# Logger.info('SwipeBehavior:touch_move fired')
if self.sb_start_point is None:
self.sb_start_point = touch.pos
else:
self.sb_end_point = touch.pos
def on_touchup(self,o,touch):
ret = False
if self.collide_point(*touch.pos) and self.swipeenable:
# Logger.info('SwipeBehavior:touch_up fired')
self.sb_end_point = touch.pos
self.sb_end_time = time.time()
self.check_context_menu()
ret = True
self.check_swipe()
self.sb_start_point = None
self.sb_end_point = None
self.sb_start_time = None
self.sb_end_time = None
def check_context_menu(self):
if not self.sb_start_time:
return
if not self.sb_end_time:
return
period_time = self.sb_end_time - self.sb_start_time
# Logger.info('SwipeBehavior:period_time=%f,threshold_time=%f',
# period_time, self.threshold_time)
if period_time >= self.threshold_time:
self.dispatch('on_context_menu')
def check_swipe(self):
if not self.sb_end_point:
return
if not self.sb_start_point:
return
if abs(self.sb_end_point[0] - self.sb_start_point[0]) > \
abs(self.sb_end_point[1] - self.sb_start_point[1]):
Logger.info('SwipeBehavior:check_swipe x>y')
if self.sb_end_point[0] - self.sb_start_point[0] >= \
self.threshold:
self.dispatch('on_swipe_right')
elif self.sb_start_point[0] - self.sb_end_point[0] >= \
self.threshold:
self.dispatch('on_swipe_left')
else:
# Logger.info('SwipeBehavior:check_swipe x<y')
if self.sb_end_point[1] - self.sb_start_point[1] >= \
self.threshold:
self.dispatch('on_swipe_up')
elif self.sb_start_point[1] - self.sb_end_point[1] >= \
self.threshold:
self.dispatch('on_swipe_down')
import time
from kivy.logger import Logger
from .mixin import register_mixin
class SwipeBehavior(object):
def __init__(self, **kwargs):
object.__init__(self)
self.swipeenable = True
self.register_event_type('on_context_menu')
self.register_event_type('on_swipe_left')
self.register_event_type('on_swipe_right')
self.register_event_type('on_swipe_up')
self.register_event_type('on_swipe_down')
self.sb_start_point = None
self.sb_end_point = None
self.sb_start_time = None
self.sb_end_time = None
self.threshold=20
self.threshold_time = 0.6
self.bind(on_touch_down=self.on_touchdown)
self.bind(on_touch_move=self.on_touchmove)
self.bind(on_touch_up=self.on_touchup)
def disable_swipe(self):
self.swipeenable = False
def enable_swipe(self):
self.swipeenable = True
def on_context_menu(self):
pass
# Logger.info('SwipeBehavior:on_context_menu fired')
def on_swipe_left(self):
pass
# Logger.info('SwipeBehavior:on_swipe_left fired')
def on_swipe_right(self):
pass
# Logger.info('SwipeBehavior:on_swipe_right fired')
def on_swipe_up(self):
pass
# Logger.info('SwipeBehavior:on_swipe_up fired')
def on_swipe_down(self):
pass
# Logger.info('SwipeBehavior:on_swipe_down fired')
def on_touchdown(self,o, touch):
if touch.is_mouse_scrolling:
# Logger.info('SwipeBehavior:is_mouse_scrolling')
return False
if not self.collide_point(touch.x, touch.y):
return False
# Logger.info('SwipeBehavior:touch_down fired')
if self.swipeenable:
# Logger.info('SwipeBehavior:touch_down fired')
touch.grab(self)
self.sb_start_point = touch.pos
self.sb_start_time = time.time()
def on_touchmove(self,o,touch):
if self.collide_point(*touch.pos) and self.swipeenable:
# Logger.info('SwipeBehavior:touch_move fired')
if self.sb_start_point is None:
self.sb_start_point = touch.pos
else:
self.sb_end_point = touch.pos
def on_touchup(self,o,touch):
ret = False
if self.collide_point(*touch.pos) and self.swipeenable and \
touch.grab_current is self:
# Logger.info('SwipeBehavior:touch_up fired')
touch.ungrab(self)
self.sb_end_point = touch.pos
self.sb_end_time = time.time()
self.check_context_menu()
ret = True
self.check_swipe()
self.sb_start_point = None
self.sb_end_point = None
self.sb_start_time = None
self.sb_end_time = None
def check_context_menu(self):
if not self.sb_start_time:
return
if not self.sb_end_time:
return
period_time = self.sb_end_time - self.sb_start_time
# Logger.info('SwipeBehavior:period_time=%f,threshold_time=%f',
# period_time, self.threshold_time)
if period_time >= self.threshold_time:
self.dispatch('on_context_menu')
def check_swipe(self):
if not self.sb_end_point:
return
if not self.sb_start_point:
return
if abs(self.sb_end_point[0] - self.sb_start_point[0]) > \
abs(self.sb_end_point[1] - self.sb_start_point[1]):
Logger.info('SwipeBehavior:check_swipe x>y')
if self.sb_end_point[0] - self.sb_start_point[0] >= \
self.threshold:
self.dispatch('on_swipe_right')
elif self.sb_start_point[0] - self.sb_end_point[0] >= \
self.threshold:
self.dispatch('on_swipe_left')
else:
# Logger.info('SwipeBehavior:check_swipe x<y')
if self.sb_end_point[1] - self.sb_start_point[1] >= \
self.threshold:
self.dispatch('on_swipe_up')
elif self.sb_start_point[1] - self.sb_end_point[1] >= \
self.threshold:
self.dispatch('on_swipe_down')
register_mixin('SwipeBehavior', SwipeBehavior)

View File

@ -48,8 +48,7 @@ def SUPER(klass, obj, kw):
super(klass, obj).__init__(**kw)
for k,v in dic.items():
try:
if v is not None:
setattr(obj, k, v)
setattr(obj, k, v)
except Exception as e:
print(f'obj={obj}, setattr(obj, "{k}","{v}") error')
print_exc()

View File

@ -1,18 +1,19 @@
{
"font_sizes":{
"smallest":1.5,
"small":2.5,
"normal":3.5,
"large":4.5,
"huge":5.5,
"hugest":6.5
},
"font_name":"normal",
"script_root":"$[workdir]$/scripts",
"root":{
"widgettype":"urlwidget",
"options":{
"url":"file://index.ui"
}
}
}
{
"font_sizes":{
"smallest":1.5,
"small":2.5,
"normal":3.5,
"large":4.5,
"huge":5.5,
"hugest":6.5
},
"font_name":"normal",
"uihome":"file://",
"script_root":"$[workdir]$/scripts",
"root":{
"widgettype":"urlwidget",
"options":{
"url":"file://index.ui"
}
}
}

View File

@ -14,6 +14,21 @@
"label":"Setting",
"icon":"{{entire_url('/imgs/setting.png')}}",
"url":"{{entire_url('t.ui')}}"
},
{
"id":"behavior1",
"label":"behavior1",
"url":"{{entire_url('boxlayout/behavior.ui')}}"
},
{
"id":"modal1",
"label":"Modal Text",
"url":"{{entire_url('boxlayout/modalbehavior.ui')}}"
},
{
"id":"boxlayout",
"label":"WBoxLayout",
"url":"{{entire_url('boxlayout/normal.ui')}}"
}
]
}

View File

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