This commit is contained in:
yumoqing 2022-11-04 03:21:12 -04:00
commit d65729b431
31 changed files with 1612 additions and 249 deletions

View File

@ -5,3 +5,5 @@ add script to support local .tmpl and .dspy file translation. it can build dynam
## version 0.3.1 ## version 0.3.1
* uses weakref to collect all the i18n widgets in i18n.py * uses weakref to collect all the i18n widgets in i18n.py
* show video play position

View File

View File

@ -0,0 +1,46 @@
from kivy.app import App
from jnius import autoclass
import kivy
from android.broadcast import BroadcastReceiver
import sys
from kivyblocks.baseWidget import VBox
BluetoothManager = autoclass('android.bluetooth.BluetoothManager')
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')
BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice')
class BluetoothFinder(VBox):
data = ListProperty([])
def __init__(self, **kw):
super().__init__(**kw)
arg = context.getSystemService(context.BLUETOOTH_SERVICE)
self.bt_manager = BluetoothManager(arg)
self.bt_adapter = self.bt_manager.getAdaper()
self.unpairedBT()
def get_paired_bt(self):
return self.bt_adapter.getBondedDevice()
def __del__(self):
self.adapter.cancelDiscovery()
def unpairedBT(self):
self.adapter = BluetoothAdapter.getDefaultAdapter()
# Search foir unpaired devices
print("Unpaired devices")
self.data=[{"text": "Unpaired Devices"}]
myReceiver = BroadcastReceiver(self.onReceive, actions = [BluetoothDevice.ACTION_FOUND, BluetoothAdapter.ACTION_DISCOVERY_STARTED, BluetoothAdapter.ACTION_DISCOVERY_FINISHED])
myReceiver.start()
self.adapter.startDiscovery()
# Called by Broadcastreceiver
def onReceive(self, context, intent):
print(f"*BT* On receive context{context}", flush = True)
print(f"*BT* On receive intent {intent}", flush = True)
sys.stdout.flush()
self.myData.append({"text": f"Context {context}, intent {intent}"})
self.layout.data = [item for item in self.myData]

View File

@ -2,6 +2,7 @@ import sys
import math import math
from traceback import print_exc from traceback import print_exc
from kivy.resources import resource_find
from kivy.properties import ObjectProperty, StringProperty, \ from kivy.properties import ObjectProperty, StringProperty, \
NumericProperty, BooleanProperty, OptionProperty NumericProperty, BooleanProperty, OptionProperty
from kivy.properties import DictProperty from kivy.properties import DictProperty
@ -67,7 +68,7 @@ from .widgetExt.inputext import FloatInput,IntegerInput, \
StrInput,SelectInput, BoolInput, Password StrInput,SelectInput, BoolInput, Password
from .widgetExt.messager import Messager from .widgetExt.messager import Messager
from .bgcolorbehavior import BGColorBehavior from .bgcolorbehavior import BGColorBehavior
from .utils import NeedLogin, InsufficientPrivilege, HTTPError from .utils import NeedLogin, InsufficientPrivilege, HTTPError, blockImage
from .login import LoginForm from .login import LoginForm
from .tab import TabsPanel from .tab import TabsPanel
from .threadcall import HttpClient from .threadcall import HttpClient
@ -76,6 +77,27 @@ from .widget_css import WidgetCSS
from .ready import WidgetReady from .ready import WidgetReady
from .utils import CSize, SUPER from .utils import CSize, SUPER
from .swipebehavior import SwipeBehavior from .swipebehavior import SwipeBehavior
from .widgetExt.inputext import MyDropDown
font_names = {
'text':resource_find('DroidSansFallback.ttf'),
'title6':resource_find('TsangerYuYangT_W01_W01.ttf'),
'title5':resource_find('TsangerYuYangT_W01_W02.ttf'),
'title4':resource_find('TsangerYuYangT_W01_W03.ttf'),
'title3':resource_find('TsangerYuYangT_W01_W04.ttf'),
'title2':resource_find('TsangerYuYangT_W01_W05.ttf'),
'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': if platform == 'android':
from .widgetExt.phonebutton import PhoneButton from .widgetExt.phonebutton import PhoneButton
@ -145,33 +167,23 @@ class Text(Label):
def __init__(self,i18n=False, texttype='text', wrap=False, def __init__(self,i18n=False, texttype='text', wrap=False,
fgcolor=None, **kw): fgcolor=None, **kw):
fontsize={'font_size':CSize(1)} fontsize = font_sizes.get(texttype)
offset={ fontname = font_names.get(texttype)
'text':0,
'title1':CSize(0.6),
'title2':CSize(0.5),
'title3':CSize(0.4),
'title4':CSize(0.3),
'title5':CSize(0.2),
'title6':CSize(0.1),
}
fontsize = {'font_size': CSize(1) + offset.get(texttype,0)}
self._i18n = i18n self._i18n = i18n
self.i18n = I18n() self.i18n = I18n()
self.bgcolor = fgcolor self.bgcolor = fgcolor
kwargs = kw.copy() kwargs = kw.copy()
config = getConfig() config = getConfig()
self.wrap = wrap self.wrap = wrap
if kwargs.get('font_size') and texttype=='text': kwargs.update({
pass 'font_size':fontsize,
else: 'font_name':fontname
kwargs.update(fontsize) })
if not kwargs.get('text'): if not kwargs.get('text'):
kwargs['text'] = kwargs.get('otext','') kwargs['text'] = kwargs.get('otext','')
SUPER(Text, self, kwargs) SUPER(Text, self, kwargs)
if self._i18n: if self._i18n:
self.i18n = I18n()
self.i18n.addI18nWidget(self) self.i18n.addI18nWidget(self)
if self.wrap: if self.wrap:
self.size_hint_y = None self.size_hint_y = None
@ -220,7 +232,8 @@ class Text(Label):
self.lang = lang self.lang = lang
def on_lang(self,o,lang): def on_lang(self,o,lang):
self.text = self.i18n(self.otext) if self._i18n and self.otext:
self.text = self.i18n(self.otext)
class Title1(Text): class Title1(Text):
def __init__(self, **kw): def __init__(self, **kw):
@ -256,12 +269,19 @@ class Modal(VBox):
content = DictProperty(None) content = DictProperty(None)
auto_open = BooleanProperty(True) auto_open = BooleanProperty(True)
auto_dismiss = BooleanProperty(True) auto_dismiss = BooleanProperty(True)
position = OptionProperty('tc',options=['tl', 'tc', 'tr', target = StringProperty(None)
position = OptionProperty('cc',options=['tl', 'tc', 'tr',
'cl', 'cc', 'cr', 'cl', 'cc', 'cr',
'bl', 'bc', 'br']) 'bl', 'bc', 'br'])
def __init__(self, **kw): def __init__(self, **kw):
SUPER(Modal, self, kw) 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')
if self.content: if self.content:
blocks = Factory.Blocks() blocks = Factory.Blocks()
self.content_w = blocks.widgetBuild(self.content) self.content_w = blocks.widgetBuild(self.content)
@ -269,10 +289,6 @@ class Modal(VBox):
self.add_widget(self.content_w) self.add_widget(self.content_w)
else: else:
print(content,':cannot build widget') print(content,':cannot build widget')
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')
def on_touch_down(self, touch): def on_touch_down(self, touch):
if not self.collide_point(touch.x, touch.y): if not self.collide_point(touch.x, touch.y):
@ -283,41 +299,74 @@ class Modal(VBox):
return super().on_touch_down(touch) return super().on_touch_down(touch)
def set_modal_position(self, w): def on_target(self):
w = Window
if self.target is not None:
w = Factory.Blocks.getWidgetById(self.target)
if w is None:
w = Window
if w != self._target:
self._target = w
def set_target(self):
if self._target is None:
if self.target is None:
w = Window
else:
w = Factory.Blocks.getWidgetById(self.target)
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
print(self.width, self.height,
self.size_hint_x, self.size_hint_y,
self._target.size
)
self.set_modal_position()
def set_modal_position(self):
self.set_target()
xn = self.position[1] xn = self.position[1]
yn = self.position[0] yn = self.position[0]
x, y = 0, 0 x, y = 0, 0
if xn == 'c': if xn == 'c':
x = (w.width - self.width) / 2 x = (self._target.width - self.width) / 2
elif xn == 'r': elif xn == 'r':
x = w.width - self.width x = self._target.width - self.width
if x < 0: if x < 0:
x = 0 x = 0
if yn == 'c': if yn == 'c':
y = (w.height - self.height) / 2 y = (self._target.height - self.height) / 2
elif yn == 'b': elif yn == 't':
y = w.height - self.height y = self._target.height - self.height
if y < 0: if y < 0:
y = 0 y = 0
if w == Window: if self._target == Window:
self.pos = x, y self.pos = x, y
else: else:
self.pos = w.pos[0] + x, w.pos[1] + y self.pos = self._target.pos[0] + x, self._target.pos[1] + y
def open(self, widget=None): def open(self):
if self.parent: if self.parent:
return self.parent.remove_widget(self)
self.dispatch('on_pre_open') self.dispatch('on_pre_open')
if widget is None:
widget = Window
self.set_modal_position(widget)
Window.add_widget(self) Window.add_widget(self)
self.dispatch('on_open') self.dispatch('on_open')
if self._target != Window:
self._target.disabled = True
def dismiss(self, *args): def dismiss(self, *args):
self.dispatch('on_pre_dismiss') self.dispatch('on_pre_dismiss')
self.dispatch('on_dismiss') self.dispatch('on_dismiss')
Window.remove_widget(self) Window.remove_widget(self)
if self._target != Window:
self._target.enabled = False
def on_open(self, *args): def on_open(self, *args):
pass pass
@ -333,7 +382,6 @@ class Modal(VBox):
def add_widget(self, w, *args, **kw): def add_widget(self, w, *args, **kw):
super().add_widget(w, *args, **kw) super().add_widget(w, *args, **kw)
# super().add_widget(Label(text='1111'))
if self.auto_open: if self.auto_open:
self.open() self.open()
@ -358,6 +406,32 @@ class TimedModal(Modal):
self.time_task = None self.time_task = None
super().dismiss() super().dismiss()
class Running(AsyncImage):
def __init__(self, widget, **kw):
super().__init__(**kw)
self.host_widget = widget
self.source = blockImage('running.gif')
self.host_widget.bind(size=self.set_size)
self.set_size()
self.open()
def open(self):
if self.parent:
self.parent.remove_widget(self)
Window.add_widget(self)
self.host_widget.disabled = True
def dismiss(self):
if self.parent:
self.parent.remove_widget(self)
self.host_widget.disabled = False
def set_size(self, *args):
self.size_hint = (None, None)
self.width = CSize(2)
self.height = CSize(2)
self.center = self.host_widget.center
class PressableImage(ButtonBehavior,AsyncImage): class PressableImage(ButtonBehavior,AsyncImage):
def on_press(self): def on_press(self):
pass pass
@ -366,6 +440,10 @@ class PressableLabel(ButtonBehavior, Text):
def on_press(self): def on_press(self):
pass pass
class PressableText(ButtonBehavior, Text):
def on_press(self):
pass
class FILEDataHandler(EventDispatcher): class FILEDataHandler(EventDispatcher):
def __init__(self, url, suffixs=[],params={}): def __init__(self, url, suffixs=[],params={}):
self.url = url self.url = url
@ -566,3 +644,29 @@ class Slider(Carousel):
w = bk.widgetBuild(desc) w = bk.widgetBuild(desc)
self.add_widget(w) self.add_widget(w)
class I18nWidget(PressableText):
lang = StringProperty(None)
def __init__(self, **kw):
super().__init__(**kw)
i18n = I18n()
self.lang = i18n.lang
def on_lang(self, *args):
self.otext = self.lang
def on_press(self, *args):
i18n = I18n()
langs = i18n.get_languages()
data = [ {'lang':l} for l in langs ]
mdd = MyDropDown(textField='lang', valueField='lang',
value=self.lang,
data=data)
mdd.bind(on_select=self.selected_lang)
mdd.showme(self)
def selected_lang(self, o, v):
lang = v[0]
self.lang = lang
i18n = I18n()
i18n.changeLang(self.lang)

View File

@ -1,3 +1,4 @@
<<<<<<< HEAD
import os import os
import sys import sys
import codecs import codecs
@ -818,3 +819,822 @@ class {{ classname }}({% for b in bases -%}{{b}}{% endfor %})
Factory.register('Blocks',Blocks) Factory.register('Blocks',Blocks)
Factory.register('Video',Video) Factory.register('Video',Video)
Factory.register('OrientationLayout', OrientationLayout) Factory.register('OrientationLayout', OrientationLayout)
=======
import os
import sys
import codecs
# import ujson as json
try:
import ujson as json
except:
import json
from traceback import print_exc
from functools import partial
from appPublic.dictExt import dictExtend
from appPublic.folderUtils import ProgramPath
from appPublic.dictObject import DictObject
from appPublic.Singleton import SingletonDecorator, GlobalEnv
from appPublic.datamapping import keyMapping
from appPublic.registerfunction import RegisterFunction
from kivy.logger import Logger
from kivy.config import Config
from kivy.metrics import sp,dp,mm
from kivy.core.window import WindowBase, Window
from kivy.properties import BooleanProperty
from kivy.uix.widget import Widget
from kivy.clock import mainthread
from kivy.uix.modalview import ModalView
from kivy.app import App
from kivy.factory import Factory
from kivy.uix.video import Video
from .utils import *
from .newvideo import Video
from .orientationlayout import OrientationLayout
from .threadcall import HttpClient
from .register import *
from .script import Script, set_script_env
class WidgetNotFoundById(Exception):
def __init__(self, id):
super().__init__()
self.idstr = id
def __str__(self):
return "Widget not found by id:" + self.idstr + ':'
def __expr__(self):
return str(self)
class ClassMethodNotFound(Exception):
def __init__(self,k,m):
super().__init__()
self.kname = k
self.mname = m
def __str__(self):
s = 'Method(%s) not found in class(%s)' % (self.mname,
str(self.kname.__classname__))
return s
def __expr__(self):
return self.__str__()
class NotExistsObject(Exception):
def __init__(self,name):
super().__init__()
self.name = name
def __str__(self):
s = 'not exists widget(%s)' % self.name
return s
def __expr__(self):
return self.__str__()
class ArgumentError(Exception):
def __init__(self,argument,desc):
super().__init__()
self.argument = argument
self.desc = desc
def __str__(self):
s = 'argument(%s) missed:%s' % (self.argument,self.desc)
return s
def __expr__(self):
return self.__str__()
class NotRegistedWidget(Exception):
def __init__(self, name:str):
super().__init__()
self.widget_name = name
def __str__(self):
s = 'not reigsted widget(%s)' % self.name
return s
def __expr__(self):
return self.__str__()
def registerWidget(name:str,widget):
globals()[name] = widget
class Blocks(EventDispatcher):
def __init__(self):
EventDispatcher.__init__(self)
self.action_id = 0
self.register_event_type('on_built')
self.register_event_type('on_failed')
self.env = GlobalEnv()
config = getConfig()
self.script = Script()
def set(self, k:str, v):
self.env[k] = v
def register_widget(self, name:str, widget:Widget):
globals()[name] = widget
def buildAction(self, widget:Widget, desc):
conform_desc = desc.get('conform')
blocks = Blocks()
if not conform_desc:
return partial(blocks.uniaction, widget, desc)
func =partial(blocks.conform_action,widget, desc)
return func
def eval(self, s:str, l:dict):
g = {}
forbidens = [
"os",
"sys",
"codecs",
"json",
]
for k,v in globals().copy().items():
if k not in forbidens:
g[k] = v
"""
g['__builtins__'] = globals()['__builtins__']
g['__builtins__']['__import__'] = None
g['__builtins__']['__loader__'] = None
g['__builtins__']['open'] = None
"""
g.update(self.env)
return eval(s,g,l)
def getUrlData(self, url:str, method:str='GET',
params:dict={}, files:dict={},
callback=None,
errback=None,**kw):
if url is None:
if errback:
errback(None,Exception('url is None'))
else:
return None
if url.startswith('file://'):
return self.script.dispatch(url, **params)
elif url.startswith('http://') or url.startswith('https://'):
try:
hc = HttpClient()
resp=hc(url,method=method,params=params,files=files)
# print('Blocks.py :resp=',resp)
return resp
except Exception as e:
print_exc()
if errback:
return errback(None,e)
return None
else:
config = getConfig()
url = config.uihome + url
return self.getUrlData(url,method=method,
params=params,
files=files,
**kw)
def strValueExpr(self,s:str,localnamespace:dict={}):
if not s.startswith('py::'):
return s
s = s[4:]
try:
v = self.eval(s,localnamespace)
return v
except Exception as e:
print('Exception .... ',e,'script=',s)
print_exc()
return s
def arrayValueExpr(self,arr:list,localnamespace:dict={}) -> list:
d = []
for v in arr:
if type(v) == type(''):
d.append(self.strValueExpr(v,localnamespace))
continue
if type(v) == type([]):
d.append(self.arrayValueExpr(v,localnamespace))
continue
if type(v) == type({}):
d.append(self.dictValueExpr(v,localnamespace))
continue
if type(v) == type(DictObject):
d.append(self.dictValueExpr(v,localnamespace))
continue
d.append(v)
return d
def dictValueExpr(self,dic:dict,localnamespace:dict={}) -> dict:
d = {}
for k,v in dic.items():
if type(v) == type(''):
d[k] = self.strValueExpr(v,localnamespace)
continue
if type(v) == type([]):
d[k] = self.arrayValueExpr(v,localnamespace)
continue
if type(v) == type({}):
d[k] = self.dictValueExpr(v,localnamespace)
continue
if type(v) == type(DictObject):
d[k] = self.dictValueExpr(v,localnamespace)
continue
d[k] = v
return d
def valueExpr(self,obj,localnamespace:dict={}):
if type(obj) == type(''):
return self.strValueExpr(obj,localnamespace)
if type(obj) == type([]):
return self.arrayValueExpr(obj,localnamespace)
if type(obj) == type({}):
return self.dictValueExpr(obj,localnamespace)
if isinstance(obj,DictObject):
return self.dictValueExpr(obj,localnamespace)
return obj
def w_build(self,desc) -> 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())
widget = None
try:
klass = Factory.get(widgetClass)
widget = klass(**opts)
except Exception as e:
print('Error:',widgetClass,'contructon error')
print_exc()
raise NotExistsObject(widgetClass)
if desc.get('id'):
id = desc.get('id')
if id.startswith('app.'):
app = App.get_running_app()
id = id[4:]
setattr(app, id, widget)
if id.startswith('root.'):
app = App.get_running_app()
id = id[5:]
setattr(app.root, id, widget)
if '.' in id:
Logger.info('widget id(%s) can not contain "."', id)
else:
widget.widget_id = id
widget.build_desc = desc
self.build_attributes(widget, desc)
self.build_rest(widget, desc)
self.buildBinds(widget, desc)
return widget
def build_attributes(self, widget:Widget,desc,t=None):
excludes = ['widgettype','options','subwidgets','binds']
for k,v in [(k,v) for k,v in desc.items() if k not in excludes]:
if isinstance(v,dict) and v.get('widgettype'):
b = Blocks()
v = self.valueExpr(v, localnamespace={'self':widget})
w = b.widgetBuild(v)
if hasattr(widget,k):
aw = getattr(widget,k)
if isinstance(aw,Layout):
aw.add_widget(w)
continue
setattr(widget,k,w)
continue
setattr(widget,k,self.valueExpr(v,\
localnamespace={'self':widget}))
def build_rest(self, widget:Widget,desc,t=None):
self.subwidget_total = len(desc.get('subwidgets',[]))
self.subwidgets = [ None for i in range(self.subwidget_total)]
pos = 0
for pos,sw in enumerate(desc.get('subwidgets',[])):
b = Blocks()
if isinstance(sw, str):
# w = Blocks.getWidgetById(sw, from_widget=widget)
w = Blocks.findWidget(sw, from_widget=widget)
if w:
widget.add_widget(w)
continue
kw = self.valueExpr(sw.copy(),
localnamespace={'self':widget})
w = b.widgetBuild(kw)
if w:
widget.add_widget(w)
def buildBinds(self, widget:Widget, desc:dict):
for b in desc.get('binds',[]):
kw = self.valueExpr(b.copy(), \
localnamespace={'self':widget})
self.buildBind(widget,kw)
def buildBind(self, widget:Widget, desc:dict):
wid = desc.get('wid','self')
# w = Blocks.getWidgetById(desc.get('wid','self'),from_widget=widget)
w = Blocks.findWidget(desc.get('wid','self'),from_widget=widget)
if not w:
Logger.info('Block: id(%s) %s',desc.get('wid','self'),
'not found via Blocks.getWidgetById()')
return
event = desc.get('event')
if event is None:
Logger.info('Block: binds desc miss event, desc=%s',str(desc))
return
f = self.buildAction(widget,desc)
if f is None:
Logger.info('Block: get a null function,%s',str(desc))
return
w.bind(**{event:f})
def multipleAction(self, widget:Widget, desc, *args):
desc1 = {k:v for k, v in desc.items() if k != 'actions'}
mydesc = desc1.copy()
for a in desc['actions']:
new_desc = mydesc.copy()
new_desc.update(a)
self.uniaction(widget,new_desc, *args)
def conform_action(self, widget:Widget, desc, *args):
conform_desc = desc.get('conform')
blocks = Blocks()
if not conform_desc:
blocks.uniaction(widget, desc,*args, **kw)
return
w = blocks.widgetBuild({
"widgettype":"Conform",
"options":conform_desc
})
w.bind(on_conform=partial(blocks.uniaction, widget, desc))
w.open()
def uniaction(self, widget:Widget, desc, *args):
acttype = desc.get('actiontype')
if acttype=='blocks':
return self.blocksAction(widget,desc, *args)
if acttype=='urlwidget':
return self.urlwidgetAction(widget,desc, *args)
if acttype == 'registedfunction':
return self.registedfunctionAction(widget,desc, *args)
if acttype == 'script':
return self.scriptAction(widget, desc, *args)
if acttype == 'method':
return self.methodAction(widget, desc, *args)
if acttype == 'event':
return self.eventAction(widget, desc, *args)
if acttype == 'multiple':
return self.multipleAction(widget, desc, *args)
alert("actiontype(%s) invalid" % acttype,title='error')
def eventAction(self, widget:Widget, desc, *args):
target = self.get_target(widget, desc)
event = desc.get('dispatch_event')
if not event:
Logger.info('Block: eventAction():desc(%s) miss dispatch_event',
str(desc))
return
params = desc.get('params',{})
d = self.getActionData(widget,desc, *args)
if d:
params.update(d)
try:
target.dispatch(event, params)
except Exception as e:
Logger.info(f'Block: eventAction():dispatch {event} error')
print_exc()
return
def get_target(self, widget:Widget, desc):
if not desc.get('target'):
return None
# return Blocks.getWidgetById(desc.get('target'),from_widget=widget)
return Blocks.findWidget(desc.get('target'),from_widget=widget)
def blocksAction(self, widget:Widget, desc, *args):
target = self.get_target(widget, desc)
add_mode = desc.get('mode','replace')
opts = desc.get('options').copy()
d = self.getActionData(widget,desc, *args)
p = opts.get('options',{}).copy()
if d:
p.update(d)
opts['options'] = p
def doit(target:Widget, add_mode:str, o, w:Widget):
if isinstance(w, Modal):
return
if target and not w.parent:
if add_mode == 'replace':
target.clear_widgets()
target.add_widget(w)
def doerr(o,e):
Logger.info('Block: blocksAction(): desc=%s widgetBuild error'
,str(desc))
raise e
b = Blocks()
b.bind(on_built=partial(doit,target,add_mode))
b.bind(on_failed=doerr)
b.widgetBuild(opts)
def urlwidgetAction(self, widget:Widget, desc, *args):
target = self.get_target(widget, desc)
add_mode = desc.get('mode','replace')
opts = desc.get('options', {}).copy()
p1 = opts.get('params',{})
p = {}
if p1:
p.update(p1)
if len(args) >= 1 and isinstance(args[0],dict):
p.update(args[0])
d = self.getActionData(widget, desc, *args)
if d:
p.update(d)
opts['params'] = p
d = {
'widgettype' : 'urlwidget',
'options': opts
}
def doit(target:Widget, add_mode:str, o, w:Widget):
if isinstance(w, ModalView):
return
if target and not w.parent:
if add_mode == 'replace':
target.clear_widgets()
target.add_widget(w)
def doerr(o,e):
Logger.info('Block: urlwidgetAction(): desc=%s widgetBuild error'
,str(desc))
b = Blocks()
b.bind(on_built=partial(doit,target,add_mode))
b.bind(on_failed=doerr)
b.widgetBuild(d)
def getActionData(self, widget:Widget, desc, *args):
data = {}
rtdesc = self.build_rtdesc(desc)
if rtdesc:
rt = self.get_rtdata(widget, rtdesc, *args)
if rt:
data.update(rt)
if desc.get('keymapping'):
data = keyMapping(data, desc.get('keymapping'))
Logger.info('getActionData():rtdesc=%s, data=%s', rtdesc, data)
return data
def registedfunctionAction(self, widget:Widget, desc, *args):
target = self.get_target(widget, desc)
rf = RegisterFunction()
name = desc.get('rfname')
func = rf.get(name)
if func is None:
Logger.info('Block: desc=%s rfname(%s) not found',
str(desc), name)
raise Exception('rfname(%s) not found' % name)
params = desc.get('params',{}).copy()
d = self.getActionData(widget,desc, *args)
if d:
params.update(d)
func(target, *args, **params)
def scriptAction(self, widget:Widget, desc, *args):
script = desc.get('script')
if not script:
Logger.info('Block: scriptAction():desc(%s) target not found',
str(desc))
return
target = self.get_target(widget, desc)
d = self.getActionData(widget,desc, *args)
ns = {
"self":target,
"args":args,
"kwargs":d
}
if d:
ns.update(d)
try:
self.eval(script, ns)
except Exception as e:
print_exc()
print(e)
def build_rtdesc(self, desc):
rtdesc = desc.get('rtdata')
if not rtdesc:
if desc.get('datawidget'):
rtdesc = {}
rtdesc['target'] = desc['datawidget']
if desc.get('datascript'):
rtdesc['script'] = desc.get('datacript')
else:
rtdesc['method'] = desc.get('datamethod', 'getValue')
rtdesc['kwargs'] = desc.get('datakwargs', {})
return rtdesc
def get_rtdata(self, widget:Widget, desc, *args):
"""
desc descript follow attributes
{
"target", a target name from widget
"method", a method name, default is 'getValue',
"script", script to handle the data
"kwargs":" a dict arguments, default is {}
}
"""
if desc is None or desc == {}:
print('get_rtdata():desc is None')
return {}
kwargs = desc.get('kwargs', {})
if not isinstance(kwargs, dict):
kwargs = {}
w = None
if len(args) > 0:
w = args[0]
target = desc.get('target')
if target:
# w = Blocks.getWidgetById(target, from_widget=widget)
w = Blocks.findWidget(target, from_widget=widget)
if w is None:
print('get_rtdata():w is None', desc)
return {}
script = desc.get('script')
if script:
ns = {
"self":w,
"args":args,
"kwargs":kwargs
}
d = self.eval(script, ns)
return d
method = desc.get('method', 'getValue')
if not hasattr(w, method):
print('get_rtdata():method is None', desc, type(w))
return {}
f = getattr(w, method)
try:
r = f(**kwargs)
if isinstance(r, dict):
return r
if isinstance(r, DictObject):
return r.to_dict()
print('get_rtdata():method return is not dict', desc, 'ret=', r, type(r))
return {}
except:
print_exc()
return {}
def methodAction(self, widget:Widget, desc, *args):
method = desc.get('method')
target = self.get_target(widget, desc)
if target is None:
Logger.info('Block: methodAction():desc(%s) target not found',
str(desc))
return
if method is None:
Logger.info('Block: methodAction():desc(%s) method not found',
str(desc))
return
if hasattr(target,method):
f = getattr(target, method)
kwargs = desc.get('options',{}).copy()
d = self.getActionData(widget,desc, *args)
if d:
kwargs.update(d)
f(*args, **kwargs)
else:
alert('%s method not found' % method)
def widgetBuild(self, desc):
"""
desc format:
{
widgettype:<registered widget>,
id:widget id,
options:{}
subwidgets:[
]
binds:[
]
}
"""
def doit(desc):
if isinstance(desc,DictObject):
desc = desc.to_dict()
if not isinstance(desc,dict):
Logger.info('Block: desc(%s) must be a dict object(%s)',
desc,type(desc))
return None
# desc = self.valueExpr(desc)
try:
widget = self.w_build(desc)
self.dispatch('on_built',widget)
if hasattr(widget,'ready'):
widget.ready = True
return widget
except Exception as e:
print_exc()
self.dispatch('on_failed',e)
return None
if isinstance(desc,DictObject):
desc = desc.to_dict()
if not isinstance(desc, dict):
print('widgetBuild1: desc must be a dict object',
desc,type(desc))
self.dispatch('on_failed',Exception('miss url'))
return
widgettype = desc.get('widgettype')
while widgettype == "urlwidget":
opts = desc.get('options',{}).copy()
extend = desc.get('extend')
addon = None
if desc.get('extend'):
addon = desc.get('extend').copy()
url = opts.get('url')
if url is None:
self.dispatch('on_failed',Exception('miss url'))
return
if opts.get('url'):
del opts['url']
rtdesc = self.build_rtdesc(opts)
if rtdesc:
rtdata = self.get_rtdata(None, rtdesc)
params = opts.get('params', {})
params.update(rtdata)
opts['params'] = params
desc = self.getUrlData(url,**opts)
if not (isinstance(desc, DictObject) or \
isinstance(desc, dict)):
print('Block2: desc must be a dict object',
desc,type(desc))
self.dispatch('on_failed',Exception('miss url'))
return
if addon:
desc = dictExtend(desc,addon)
widgettype = desc.get('widgettype')
if widgettype is None:
print('Block3: desc must be a dict object not None')
return None
return doit(desc)
@classmethod
def findWidget(self, id:str, from_widget:Widget=None) -> Widget:
"""
-:find up direct, find widget in ancestor
+ or empty: find down direct, find widget in descendant
@:find widget by class
$ or empty:find widget by id
.:more than one step.
"""
def find_widget_by_id(id, from_widget):
if id=='self':
return from_widget
if hasattr(from_widget,'widget_id'):
if from_widget.widget_id == id:
return from_widget
if hasattr(from_widget, id):
w = getattr(from_widget,id)
return w
return None
def find_widget_by_class(klass, from_widget):
if from_widget.__class__.__name__ == klass:
return from_widget
for k, v in from_widget.__dict__.items():
if isinstance(v, Widget):
if v.__class__.__name__ == klass:
return v
return None
def _find_widget(name, from_widget, dir='down',
find_func=find_widget_by_id):
w = find_func(name, from_widget)
if w:
return w
if dir == 'down':
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))
for c in children:
ret = _find_widget(name, from_widget=c, dir=dir)
if ret:
return ret
else:
if isinstance(from_widget, WindowBase):
return None
if from_widget.parent:
return _find_widget(name,
from_widget=from_widget.parent,
dir=dir)
return None
def find_widget(step, from_widget):
dir = 'down'
if step[0:1] == '-':
dir = 'up'
step = step[1:]
find_func = find_widget_by_id
if step[0:1] == '@':
step = step[1:]
find_func = find_widget_by_class
return _find_widget(step, from_widget=from_widget,
dir=dir, find_func=find_func)
steps = id.split('.')
w = from_widget
for i, s in enumerate(steps):
if i==0:
app = App.get_running_app()
fid = s
if fid == '/self' or fid == 'root':
w = app.root
if len(steps) == 1:
return from_widget
continue
if fid == 'Window':
w = Window
if len(steps) == 1:
return w
continue
if fid == 'app':
return app
w = find_widget(s, w)
if not w:
return None
return w
@classmethod
def getWidgetById(self, id:str, from_widget:Widget=None) -> Widget:
return Blocks.findWidget(id, from_widget=from_widget)
def on_built(self,v=None):
return
def on_failed(self,e=None):
return
def buildKlass(self, desc):
"""
desc = {
"classname":"MyClass",
"base":["Box", "WidgetReady"],
"properties":[
{
"name":"aaa",
"type":"str",
"default":1
}
]
"subwidgets":[
],
"""
codes = """
{% for b in bases %}
{{b}} = Factory.{{b}}
{% endfor %}
class {{ classname }}({% for b in bases -%}{{b}}{% endfor %})
{% for p in properties %}
{{p.name}} {{p.type}}({{p.default}})
{% endfor %}
def __init__(self, **kw):
super({{classname}}, self).__init__(**kw)
"""
Factory.register('Blocks',Blocks)
Factory.register('Video',Video)
Factory.register('OrientationLayout', OrientationLayout)
>>>>>>> cab6a2818466e5d32880a6a01e7afe16ad192675

View File

@ -2,12 +2,6 @@
import os import os
import sys import sys
from kivy.resources import resource_add_path from kivy.resources import resource_add_path
from kivy.config import Config
resource_add_path(os.path.join(os.path.dirname(__file__),'./ttf'))
Config.set('kivy', 'default_font', [
'msgothic',
'DroidSansFallback.ttf'])
import signal import signal
import codecs import codecs
import json import json
@ -23,6 +17,7 @@ from kivy.core.window import WindowBase, Window
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger from kivy.logger import Logger
from kivy.utils import platform from kivy.utils import platform
from kivy.metrics import Metrics
from kivy.app import App from kivy.app import App
import plyer import plyer
@ -31,20 +26,25 @@ from .register import *
from .threadcall import HttpClient,Workers from .threadcall import HttpClient,Workers
from .utils import * from .utils import *
from .widget_css import register_css from .widget_css import register_css
from .version import __version__
if platform == 'android': if platform == 'android':
from android.storage import app_storage_path
from jnius import autoclass from jnius import autoclass
from .android_rotation import get_rotation
from .android_rotation import get_rotation Logger.info(f'KivyBlocks:version={__version__}')
def signal_handler(signal, frame): def signal_handler(signal, frame):
app = App.get_running_app() app = App.get_running_app()
if app is None:
return
app.workers.running = False app.workers.running = False
app.stop() app.stop()
print('Singal handled .........') print('Singal handled .........')
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
class BlocksApp(App): class BlocksApp(App):
def get_rotation(self): def get_rotation(self):
return get_rotation() return get_rotation()
@ -80,6 +80,8 @@ class BlocksApp(App):
self.public_headers = { self.public_headers = {
"platform":self.platform "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_request_close=self.on_close)
Window.bind(on_rotate=self.on_rotate) Window.bind(on_rotate=self.on_rotate)
Window.bind(size=self.device_info) Window.bind(size=self.device_info)
@ -104,23 +106,19 @@ class BlocksApp(App):
def get_user_data_path(self): def get_user_data_path(self):
if platform == 'android': if platform == 'android':
Environment = autoclass('android.os.Environment') # Environment = autoclass('android.os.Environment')
sdpath = Environment.getExternalStorageDirectory() # sdpath = Environment.getExternalStorageDirectory()
return str(sdpath) # return str(sdpath)
return str(app_storage_path())
sdpath = App.get_running_app().user_data_dir sdpath = App.get_running_app().user_data_dir
return str(sdpath) return str(sdpath)
def get_profile_name(self): def get_profile_name(self):
fname = os.path.join(self.user_data_dir,'.profile.json') fname = os.path.join(self.get_user_data_path(),'.profile.json')
print('profile_path=', fname) print('profile_path=', fname)
return fname return fname
def write_profile(self, dic): def default_profile(self):
fname = self.get_profile_name()
with codecs.open(fname,'w','utf-8') as f:
json.dump(dic,f)
def write_default_profile(self):
device_id = getID() device_id = getID()
try: try:
device_id = plyer.uniqueid.id device_id = plyer.uniqueid.id
@ -132,6 +130,15 @@ class BlocksApp(App):
d = { d = {
'device_id': device_id 'device_id': device_id
} }
return d
def write_profile(self, dic):
fname = self.get_profile_name()
with codecs.open(fname,'w','utf-8') as f:
json.dump(dic,f)
def write_default_profile(self):
d = self.default_profile()
self.write_profile(d) self.write_profile(d)
def read_profile(self): def read_profile(self):

View File

@ -16,6 +16,11 @@ from kivyblocks.widget_css import WidgetCSS
from .uitype.factory import UiFactory, get_value from .uitype.factory import UiFactory, get_value
from .command_action import cmd_action from .command_action import cmd_action
def on_disabled(self, *args):
pass
TouchRippleButtonBehavior.on_disabled = on_disabled
class TinyText(Text): class TinyText(Text):
def __init__(self, **kw): def __init__(self, **kw):
SUPER(TinyText, self, kw) SUPER(TinyText, self, kw)
@ -26,6 +31,46 @@ class TinyText(Text):
self.texture_update() self.texture_update()
self.size = self.texture_size self.size = self.texture_size
class PressableBox(TouchRippleButtonBehavior, Box):
normal_css = StringProperty("default")
actived_css = StringProperty("default")
box_actived = BooleanProperty(False)
def __init__(self,
border_width=1,
user_data=None,
radius=[],
**kw):
super(PressableBox, self).__init__(
padding=[border_width,
border_width,
border_width,
border_width],
radius=radius,
**kw)
self.border_width = border_width
self.user_data = user_data
self.actived = False
self.csscls = self.normal_css
def active(self, flag):
self.box_actived = flag
def on_box_actived(self, o, v):
if self.box_actived:
self.csscls = self.actived_css
else:
self.csscls = self.normal_css
def on_press(self,o=None):
self.box_actived = True
def setValue(self,d):
self.user_data = d
def getValue(self):
return self.user_data
class ClickableBox(TouchRippleButtonBehavior, Box): class ClickableBox(TouchRippleButtonBehavior, Box):
def __init__(self, def __init__(self,
border_width=1, border_width=1,

View File

@ -15,7 +15,6 @@ def get_cmd_desc(cmd_desc):
desc['datamethod'] = v['datamethod'] desc['datamethod'] = v['datamethod']
keys = v.keys() keys = v.keys()
if 'url' in keys: if 'url' in keys:
Logger.info('get_cmd_desc():cmd_desc=%s', cmd_desc)
desc['actiontype'] = 'urlwidget' desc['actiontype'] = 'urlwidget'
desc['mode'] = 'replace' desc['mode'] = 'replace'
options = { options = {
@ -24,18 +23,22 @@ def get_cmd_desc(cmd_desc):
} }
desc['options'] = options desc['options'] = options
return desc return desc
if 'rfname' in keys: if 'rfname' in keys:
desc['actiontype'] = 'registedfunction' desc['actiontype'] = 'registedfunction'
desc['params'] = v.get('params',{}) desc['params'] = v.get('params',{})
return desc return desc
if 'script' in keys: if 'script' in keys:
desc['actiontype'] = 'script' desc['actiontype'] = 'script'
desc['script'] = v.get('script') desc['script'] = v.get('script')
return desc return desc
if 'method' in keys: if 'method' in keys:
desc['actiontype'] = 'method' desc['actiontype'] = 'method'
desc['method'] = v.get('method') desc['method'] = v.get('method')
return desc return desc
if 'actions' in keys: if 'actions' in keys:
desc['actiontype'] = 'multiple' desc['actiontype'] = 'multiple'
desc['actions'] = v.get('actions') desc['actions'] = v.get('actions')
@ -61,6 +64,6 @@ def cmd_action(cmd_desc, widget):
}) })
w.bind(on_conform=partial(blocks.uniaction, widget, desc)) w.bind(on_conform=partial(blocks.uniaction, widget, desc))
w.open() w.open()
Logger.info('cmd_action():desc=%s, conform and action', desc) print('cmd_action():desc=', desc, ' conform and action', desc)

View File

@ -1,3 +1,4 @@
<<<<<<< HEAD
from kivy.event import EventDispatcher from kivy.event import EventDispatcher
from kivy.app import App from kivy.app import App
@ -191,3 +192,187 @@ class RegisterFunctionDataLoader(DataLoader):
self.dispatch('on_error', e) self.dispatch('on_error', e)
=======
from kivy.event import EventDispatcher
from kivy.app import App
from kivy.factory import Factory
from .threadcall import HttpClient
from .utils import absurl
from appPublic.registerfunction import RegisterFunction
class DataGraber(EventDispatcher):
"""
Graber format
{
"widgettype":"DataGraber",
"options":{
"dataurl":"first",
"datarfname":"second",
"target":"third",
"params":
"method":
"pagging":"default false"
}
}
if dataurl present, the DataGraber using this dataurl to get and
return data
else if datarfname present, it find a registered function named
by 'datarfname' to return data
else if datatarget present, it find the widget and uses target
method(default is 'getValue') to return data
else it return None
"""
def __init__(self, **kw):
super().__init__()
self.options = kw
self.register_event_type('on_success')
self.register_event_type('on_error')
def load(self, *args, **kw):
ret = None
while True:
try:
dataurl = self.options.get('dataurl')
if dataurl:
ret = self.loadUrlData(*args, **kw)
break
rfname = self.options.get('datarfname')
if rfname:
ret = self.loadRFData(*args, **kw)
break
target = self.options.get('datatarget')
if target:
ret = self.loadTargetData(*args, **kw)
break
except Exception as e:
self.dispatch('on_error', e)
return
if ret:
self.dispatch('on_success',ret)
else:
e = Exception('Not method to do load')
self.dispatch('on_error', e)
def loadUrlData(self, *args, **kw):
dataurl = self.options.get('dataurl')
hc = HttpClient()
params = self.options.get('params',{}).copy()
params.update(kw)
method = self.options.get('method','GET')
d = hc(dataurl, params=params,method=method)
return d
def loadRFData(self, *args, **kw):
rfname = self.options.get('datarfname')
rf = RegisterFunction()
f = rf.get(rfname)
if not f:
return None
params = self.options.get('params',{}).copy()
params.update(kw)
try:
d = f(**params)
return d
except Exception as e:
Logger.info('blocks : Exception:%s',e)
print_exc()
return None
def loadTargetData(self, *args, **kw):
target = self.options.get('datatarget')
w = Factory.Blocks.getWidgetById(target)
if not w:
return None
params = self.options.get('params',{}).copy()
params.update(kw)
method = params.get('method', 'getValue')
if not has(w, method):
return None
try:
f = getattr(w, method)
d = f()
return d
except Exception as e:
Logger.info('blocks : Exception %s', e)
print_exc()
return None
def on_success(self,d):
pass
def on_error(self,e):
pass
class DataLoader(EventDispatcher):
def __init__(self,data_user):
self.data_user = data_user
EventDispatcher.__init__(self)
self.register_event_type('on_success')
self.register_event_type('on_error')
def on_success(self,d):
pass
def on_error(self,e):
pass
def load(self):
pass
class HttpDataLoader(DataLoader):
def load(self, *args, **kw):
app = App.get_running_app()
url = self.data_user.url
method = self.data_user.method
params = self.data_user.params.copy()
params.update({
"page":self.data_user.curpage,
"rows":self.data_user.page_rows
})
hc = HttpClient()
try:
r = hc(url, method=method, params=params)
self.dispatch('on_success', r)
return r
except Exception as e:
self.dispatch('on_error', e)
class ListDataLoader(DataLoader):
def load(self, *args, **kw):
p = self.data_user.curpage
r = self.data_user.page_rows
try:
s = self.data_user.data[(p-1)*r:p*r]
d = {
"total":len(self.data_user.data),
"rows":s
}
self.dispatch('on_success', d)
return d
except Exception as e:
self.dispatch('on_error', e)
class RegisterFunctionDataLoader(DataLoader):
def load(self, *args, **kw):
rf = RegisterFunction()
try:
rfname = self.data_user.rfname
func = rf.get(rfname)
if func is None:
raise Exception('%s is not a registerfunction' % rfname)
params = {k:v for k,v in self.user_data.params.items()}
params.update({
"page":self.data_user.curpage,
"rows":self.data_user.page_rows
})
s = func(**params)
self.dispatch('on_success', s)
return s
except Exception as e:
self.dispatch('on_error', e)
>>>>>>> cab6a2818466e5d32880a6a01e7afe16ad192675

View File

@ -25,7 +25,7 @@ from appPublic.uniqueID import getID
from appPublic.myTE import string_template_render from appPublic.myTE import string_template_render
from .utils import * from .utils import *
from .baseWidget import Text, HBox, VBox from .baseWidget import Text, HBox, VBox, Running
from .scrollpanel import ScrollPanel from .scrollpanel import ScrollPanel
from .paging import Paging, RelatedLoader from .paging import Paging, RelatedLoader
from .ready import WidgetReady from .ready import WidgetReady
@ -64,7 +64,7 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout):
if self.row.header: if self.row.header:
self.csscls=self.row.part.datagrid.header_css self.csscls=self.row.part.datagrid.header_css
if desc['header']: if desc['header']:
bl = Text(i18n=True, text=str(desc['value']), bl = Text(i18n=True, otext=str(desc['value']),
font_size=CSize(1),wrap=True, font_size=CSize(1),wrap=True,
halign='left', valign='middle' halign='left', valign='middle'
) )
@ -432,7 +432,7 @@ class DataGrid(VBox):
desc = { desc = {
"widgettype":"Text", "widgettype":"Text",
"options":{ "options":{
"text":n, "otext":n,
"i18n":True, "i18n":True,
} }
} }

7
kivyblocks/download.py Normal file
View File

@ -0,0 +1,7 @@
from kivy.properties import StringProperty
class DownloadFile(ClickableIconText):
def __init__(self, **kw):
pass

View File

@ -6,12 +6,14 @@ from ffpyplayer.tools import set_log_callback
from kivy.factory import Factory from kivy.factory import Factory
from kivy.app import App from kivy.app import App
from kivy.core.window import Window
from kivy.uix.image import Image from kivy.uix.image import Image
from kivy.uix.widget import Widget from kivy.uix.widget import Widget
from kivy.clock import Clock from kivy.clock import Clock
from kivy.properties import StringProperty, BooleanProperty, \ from kivy.properties import StringProperty, BooleanProperty, \
OptionProperty, NumericProperty OptionProperty, NumericProperty
from kivy.graphics.texture import Texture from kivy.graphics.texture import Texture
from kivy.graphics import Color, Line
from kivyblocks.ready import WidgetReady from kivyblocks.ready import WidgetReady
@ -26,17 +28,17 @@ class FFVideo(WidgetReady, Image):
duration = NumericProperty(-1) duration = NumericProperty(-1)
position = NumericProperty(-1) position = NumericProperty(-1)
volume = NumericProperty(-1) volume = NumericProperty(-1)
framerate = NumericProperty(180)
in_center_focus = BooleanProperty(False) in_center_focus = BooleanProperty(False)
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._player = None self._player = None
self._update_task = None self._update_task = None
self._texture = None self.vh_task = None
self.is_black = False self.is_black = False
self.videosize = None self.videosize = None
self.timeperiod = 1.0 / self.framerate self.ff_opts = {
self.ff_opts = {} "framedrop":True
}
self.lib_opts = {} self.lib_opts = {}
self.headers_pattern = {} self.headers_pattern = {}
super(FFVideo, self).__init__(**kwargs) super(FFVideo, self).__init__(**kwargs)
@ -46,6 +48,7 @@ class FFVideo(WidgetReady, Image):
self.register_event_type('on_leave_focus') self.register_event_type('on_leave_focus')
self.register_event_type('on_enter_focus') self.register_event_type('on_enter_focus')
self.register_event_type('on_load_success') self.register_event_type('on_load_success')
self.register_event_type('on_startplay')
def on_open_failed(self, *args): def on_open_failed(self, *args):
pass pass
@ -101,10 +104,14 @@ class FFVideo(WidgetReady, Image):
def on_status(self, *args): def on_status(self, *args):
print('on_status called, ', self.status) print('on_status called, ', self.status)
if self._player is None: if self._player is None:
print('no _player, do nothing')
return return
Window.allow_screensaver = True
if self.status == 'play': if self.status == 'play':
Window.allow_screensaver = False
self._player.set_pause(False) self._player.set_pause(False)
self.video_handle()
elif self.status == 'pause': elif self.status == 'pause':
self._player.set_pause(True) self._player.set_pause(True)
elif self.status == 'stop': elif self.status == 'stop':
@ -112,20 +119,35 @@ class FFVideo(WidgetReady, Image):
else: else:
pass pass
def on_parent(self, *args):
if self.parent:
return
self._play_stop()
def on_startplay(self, *args):
pass
def on_frame(self, *args): def on_frame(self, *args):
if self._player is None: if self._player is None:
return return
if self.audio_id is None: if not self.playing:
return self.dispatch('on_startplay')
self._player.request_channel(self, 'audio', 'open', self.audio_id) self._player.request_channel( \
'audio', 'open', self.audio_id)
self.seek(self.position)
self.playing = True
if self.duration > 0:
p = self.position / self.duration * self.width
self.canvas.after.clear()
with self.canvas.after:
Color(1,1,1,1)
Line()
Line(points=[0, 0, self.width, 0], width=1)
Color(1,0,0,1)
Line(points=[0,1,p,1], width=1)
def __del__(self): def __del__(self):
if self._update_task: self._play_stop()
self._update_task.cancel()
self._update_task = None
if self._player:
self._player.close_player()
self._player = None
def set_volume(self, v): def set_volume(self, v):
if self.play_mode == 'preview': if self.play_mode == 'preview':
@ -141,23 +163,27 @@ class FFVideo(WidgetReady, Image):
self.volume = v self.volume = v
def seek(self, pts): def seek(self, pts):
self.vsync = False
if self.play_mode == 'preview': if self.play_mode == 'preview':
return return
if self._player is None: if self._player is None:
return return
if self.status != 'play': if self.status != 'play':
return return
self._player.seek(pts) self._player.seek(pts, relative=False)
self.position = self._player.get_pts() self.position = self._player.get_pts()
self.last_val = None
def mute(self, flag): def mute(self, flag=None):
if self.play_mode == 'preview': if self.play_mode == 'preview':
return return
if self._player is None: if self._player is None:
return return
if self.status != 'play': if self.status != 'play':
return return
self._player.set_mute(flag) x = self._player.get_mute()
print('Video(), mute=', x)
self._player.set_mute(not x)
def switch_audio(self): def switch_audio(self):
if self.play_mode == 'preview': if self.play_mode == 'preview':
@ -169,7 +195,9 @@ class FFVideo(WidgetReady, Image):
self._player.request_channel('audio', action='cycle') self._player.request_channel('audio', action='cycle')
def on_v_src(self, o, src): def on_v_src(self, o, src):
self._play_stop() self.status = 'stop'
self.playing = False
ff_opts = { ff_opts = {
'pause':False 'pause':False
} }
@ -194,7 +222,8 @@ class FFVideo(WidgetReady, Image):
# self._player = MediaPlayer(self.v_src) # self._player = MediaPlayer(self.v_src)
self._player = MediaPlayer(self.v_src, ff_opts=ff_opts, \ self._player = MediaPlayer(self.v_src, ff_opts=ff_opts, \
lib_opts=lib_opts) lib_opts=lib_opts)
self._play_start() # self._play_start()
self.status = 'play'
def file_opened(self, files): def file_opened(self, files):
self.v_src = files[0] self.v_src = files[0]
@ -202,7 +231,6 @@ class FFVideo(WidgetReady, Image):
def play(self): def play(self):
if self._player is None: if self._player is None:
return return
# self._player.set_pause(False)
self.status = 'play' self.status = 'play'
def pause(self): def pause(self):
@ -212,33 +240,32 @@ class FFVideo(WidgetReady, Image):
self.status = 'pause' self.status = 'pause'
def _play_start(self): def _play_start(self):
self.timepass = 0.0
self.last_frame = None self.last_frame = None
self.is_black = False self.is_black = False
self.first_play = True self.vsync = False
self._update_task = Clock.schedule_interval(self._update, self.timeperiod)
def _get_video_info(self): def _get_video_info(self):
if self.first_play: if not self.playing:
self.playing = True
meta = self._player.get_metadata() meta = self._player.get_metadata()
self.duration = meta['duration'] self.duration = meta['duration']
self.position = 0
self._out_fmt = meta['src_pix_fmt'] self._out_fmt = meta['src_pix_fmt']
self.frame_rate = meta['frame_rate'] self.frame_rate = meta['frame_rate']
self.videosize = meta['src_vid_size'] self.videosize = meta['src_vid_size']
def _play_stop(self): def _play_stop(self):
if self._player is None: if self._player:
return self._player.close_player()
self._update_task.cancel() self._player = None
self._update_task = None if self._update_task:
self._player.close_player() self._update_task.cancel()
self._player = None self._update_task = None
self.timepass = 0 if self.vh_task:
self.vh_task.cancel()
self.vh_task = None
self.next_frame = None self.next_frame = None
self.duration = -1 self.duration = -1
self.position = -1 self.position = 0
self.frame_rate = None
self.videosize = None self.videosize = None
def on_size(self, *args): def on_size(self, *args):
@ -283,7 +310,6 @@ class FFVideo(WidgetReady, Image):
fbo['tex_y'] = 0 fbo['tex_y'] = 0
fbo['tex_u'] = 1 fbo['tex_u'] = 1
fbo['tex_v'] = 2 fbo['tex_v'] = 2
self._texture = fbo.texture
dy, du, dv, _ = img.to_memoryview() dy, du, dv, _ = img.to_memoryview()
if dy and du and dv: if dy and du and dv:
self._tex_y.blit_buffer(dy, colorfmt='luminance') self._tex_y.blit_buffer(dy, colorfmt='luminance')
@ -304,35 +330,44 @@ class FFVideo(WidgetReady, Image):
self.texture = texture self.texture = texture
# print('img_size=', w, h, 'window size=', self.size) # print('img_size=', w, h, 'window size=', self.size)
def _update(self, dt): def video_handle(self, *args):
if self.last_frame is None: if self._update_task:
frame, val = self._player.get_frame() self._update_task.cancel()
if val == 'eof': self._update_task = None
print('*****EOF******') if self._player is None:
self.status = 'stop' return
self.set_black() frame, val = self._player.get_frame()
return if val == 'eof':
if val == 'pause': self.status = 'stop'
self.status = 'pause' self.set_black()
return self.last_val = None
if frame is None: return
# print('video null', time.time()) if val == 'pause':
self.set_black() self.status = 'pause'
return self.last_val = None
self.last_frame = frame return
self.video_ts = val if frame is None:
self._get_video_info() self.set_black()
self.last_val = None
self.vh_task = Clock.schedule_once(self.video_handle, 0.1)
return
self.timepass += self.timeperiod self. _get_video_info()
self.last_frame = frame
self.video_ts = val
if self.last_val is None:
self.last_val = val
self._update_task = Clock.schedule_once(self.do_update, 0)
else:
t = val - self.last_val
if t > 0:
self._update_task = Clock.schedule_once(self.do_update, t)
else:
self._update_task = Clock.schedule_once(self.do_update, 0)
def do_update(self, *args):
self.position = self._player.get_pts() self.position = self._player.get_pts()
if self.timepass < self.video_ts: self.volume = self._player.get_volume()
return
if self.timepass > self.video_ts +0.2:
self.last_frame = None
return
self.status = 'play'
img, t = self.last_frame img, t = self.last_frame
if self._out_fmt == 'yuv420p': if self._out_fmt == 'yuv420p':
self.show_yuv420(img) self.show_yuv420(img)
@ -340,5 +375,5 @@ class FFVideo(WidgetReady, Image):
self.show_others(img) self.show_others(img)
self.dispatch('on_frame', self.last_frame) self.dispatch('on_frame', self.last_frame)
self.last_frame = None self.last_frame = None
self.vh_task = Clock.schedule_once(self.video_handle, 0)
Factory.register('FFVideo', FFVideo)

View File

@ -13,31 +13,34 @@ from .scrollpanel import ScrollPanel
from .clickable import SingleCheckBox from .clickable import SingleCheckBox
from .baseWidget import Text from .baseWidget import Text
from .utils import CSize from .utils import CSize
from .command_action import cmd_action
from appPublic.registerfunction import getRegisterFunctionByName from appPublic.registerfunction import getRegisterFunctionByName
class TreeViewComplexNode(BoxLayout, TreeViewLabel): class TreeViewComplexNode(BoxLayout, TreeViewNode):
otext = StringProperty(None) otext = StringProperty(None)
font_size_c = NumericProperty(1)
node_height = NumericProperty(2)
checkbox = BooleanProperty(False) checkbox = BooleanProperty(False)
icon = StringProperty(None) icon = StringProperty(None)
data = DictProperty(None) data = DictProperty(None)
def __init__(self, **kw): def __init__(self, **kw):
super(TreeViewComplexNode, self).__init__(**kw) super(TreeViewComplexNode, self).__init__(**kw)
self.orientation = 'horizontal' self.orientation = 'horizontal'
self.size_hint_x = None
if self.checkbox: if self.checkbox:
cb = SingleCheckBox(size_hint=(None,None)) cb = SingleCheckBox(size_hint=(None,None))
cb.bind(on_press=self.set_checked) cb.bind(on_press=self.set_checked)
self.add_widget(cb) self.add_widget(cb)
if self.icon: if self.icon:
img = AsyncImage(source=self.icon, size_hint=(None,None)) img = AsyncImage(source=self.icon, size_hint=(None,None))
img.size = CSize(1,1) img.size = CSize(self.font_size_c,self.font_size_c)
self.add_widget(img) self.add_widget(img)
txt = Text(otext=self.otext, i18n=True) txt = Text(otext=self.otext, i18n=True, wrap=True,
txt.texture_update() font_size=CSize(self.font_size_c),halign='left')
txt.size_hint = (None, None)
txt.size = txt.texture_size
self.add_widget(txt) self.add_widget(txt)
self.size_hint_x = 1
self.size_hint_y = None
self.height = CSize(self.node_height)
def set_checked(self, o): def set_checked(self, o):
if not self.data: if not self.data:
@ -52,6 +55,8 @@ class Hierarchy(ScrollPanel):
params = DictProperty(None) params = DictProperty(None)
method = StringProperty('get') method = StringProperty('get')
idField = StringProperty(None) idField = StringProperty(None)
node_height = NumericProperty(2)
font_size_c = NumericProperty(1)
textField = StringProperty(None) textField = StringProperty(None)
data = ListProperty(None) data = ListProperty(None)
checkbox = BooleanProperty(False) checkbox = BooleanProperty(False)
@ -60,13 +65,17 @@ class Hierarchy(ScrollPanel):
def __init__(self, **kw): def __init__(self, **kw):
self.register_event_type('on_press') self.register_event_type('on_press')
self.tree = TreeView(hide_root=True) self.tree = TreeView(hide_root=True)
self.tree.size_hint = (None, None) self.tree.size_hint = (1, None)
self.tree.bind(on_node_expand=self.check_load_subnodes) self.tree.bind(on_node_expand=self.check_load_subnodes)
self.tree.bind(selected_node=self.node_selected) self.tree.bind(selected_node=self.node_selected)
super(Hierarchy, self).__init__(inner=self.tree, **kw) super(Hierarchy, self).__init__(inner=self.tree, **kw)
if self.url: if self.url:
self.data = self.get_remote_data() self.data = self.get_remote_data()
def on_size(self, *args):
self.tree.size_hint_x = 1
self.tree.width = self.width
def on_press(self, node): def on_press(self, node):
print('selected node=', node) print('selected node=', node)
@ -95,12 +104,14 @@ class Hierarchy(ScrollPanel):
def create_new_node(self, data, node=None): def create_new_node(self, data, node=None):
n = TreeViewComplexNode(otext=data[self.textField], n = TreeViewComplexNode(otext=data[self.textField],
checkbox=self.checkbox, checkbox=self.checkbox,
node_height=self.node_height,
font_size_c=self.font_size_c,
icon=data.get('icon') or self.icon icon=data.get('icon') or self.icon
) )
n.data = data n.data = data
n.width = self.tree.indent_start + \ # n.width = self.tree.indent_start + \
self.tree.indent_level * n.level \ # self.tree.indent_level * n.level \
+ sum([i.width for i in n.children]) # + sum([i.width for i in n.children])
if node: if node:
self.tree.add_node(n, node) self.tree.add_node(n, node)
else: else:
@ -148,50 +159,10 @@ class Menu(Hierarchy):
def on_press(self, node): def on_press(self, node):
self.tree.deselect_node() self.tree.deselect_node()
data = {} data = {}
dw = node.data.get('datawidget') data = node.data.copy()
if dw: if self.target and data.get('target') is None:
data_widget = Factory.Blocks.getWidgetById(dw) data['target'] = self.target
if data_widget: return cmd_action(data, self)
vn = node.data.get('datamethod', 'getValue')
if hasattr(data_widget, vn):
f = getattr(data_widget, vn)
data = f()
if not isinstance(data, dict):
data = {}
url = node.data.get('url')
target = Factory.Blocks.getWidgetById(node.data.get('target',self.target),self)
if url:
params = node.data.get('params',{})
params.update(data)
blocks = Factory.Blocks()
desc = {
"widgettype":"urlwidget",
"options":{
"url":url,
"params":params
}
}
w = blocks.widgetBuild(desc)
if w and target:
target.add_widget(w)
return
rfname = node.data.get('rfname')
if rfname:
f = getRegisterFunctionByName(rfname)
if f:
f(self, **data)
return
script = node.data.get('script')
if script:
target_name = node.data.get('target', self.target)
target = Factory.Blocks.getWidgetById(target_name, self)
data.update({'self':target})
if target:
eval(script,data)
return
Factory.register('Hierarchy', Hierarchy) Factory.register('Hierarchy', Hierarchy)
Factory.register('Menu', Menu) Factory.register('Menu', Menu)

View File

@ -14,6 +14,9 @@ class I18n:
def __init__(self): def __init__(self):
self.kvlang={} self.kvlang={}
self.lang = locale.getdefaultlocale()[0] self.lang = locale.getdefaultlocale()[0]
if self.lang is None:
self.lang = 'en_US'
self.languages = None
self.loadI18n(self.lang) self.loadI18n(self.lang)
self.i18nWidgets = [] self.i18nWidgets = []
@ -21,8 +24,9 @@ class I18n:
self.i18nWidgets.append(ref(w)) self.i18nWidgets.append(ref(w))
def loadI18nFromI18nFolder(self, lang): def loadI18nFromI18nFolder(self, lang):
config = gtConfig() config = getConfig()
fpath = os.path.join(config.i18n_folder,lang,'msg.txt') fpath = os.path.join(config.i18n_folder,lang,'msg.txt')
print('fpath=', fpath, type(fpath))
with codecs.open(fpath,'r','utf-8') as f: with codecs.open(fpath,'r','utf-8') as f:
line = f.readline() line = f.readline()
d = {} d = {}
@ -32,15 +36,39 @@ class I18n:
continue continue
k,v = line.split(':',1) k,v = line.split(':',1)
d.update({k:v}) d.update({k:v})
line = readline() line = f.readline()
return d return d
def get_languages(self):
if self.languages:
return self.languages
self.languages = self.getLanguages()
return self.languages
def getLanguages(self):
config = getConfig()
if config.i18n_folder:
langs = []
for f in os.listdir(config.i18n_folder):
p = os.path.join(config.i18n_folder, f)
if os.path.isdir(p):
langs.append(os.path.basename(f))
return langs
if config.i18n_url:
url = '%s%s' % (config.uihome, config.i18n_url)
hc = HttpClient()
d = hc.get(url)
if isinstance(d, list):
return d
return []
def loadI18n(self,lang): def loadI18n(self,lang):
app = App.get_running_app() app = App.get_running_app()
config = getConfig() config = getConfig()
self.kvlang[lang] = {} self.kvlang[lang] = {}
if config.i18n_folder: if config.i18n_folder:
self.kvlang[lang] = self.loadI18nFromFolder(lang) self.kvlang[lang] = self.loadI18nFromI18nFolder(lang)
return return
if config.i18n_url: if config.i18n_url:
@ -63,8 +91,8 @@ class I18n:
if not d: if not d:
self.loadI18n(lang) self.loadI18n(lang)
self.lang = lang self.lang = lang
ws = [ w for w in self.i18nWidgets if w is not None ] ws = [ w for w in self.i18nWidgets if w() is not None ]
for w in ws:
w().changeLang(lang)
self.i18nWidgets = ws self.i18nWidgets = ws
for w in self.i18nWidgets:
w.changeLang(lang)

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
kivyblocks/imgs/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -191,7 +191,7 @@ class MarkdownBody(VBox):
self.md_obj = md_obj self.md_obj = md_obj
super().__init__(**kw) super().__init__(**kw)
self.padding=padding self.padding=padding
self.size_hint = None,None self.size_hint = 1,None
self.bind(parent=self.resize) self.bind(parent=self.resize)
self.resize() self.resize()
@ -205,6 +205,7 @@ class MarkdownBody(VBox):
f(v) f(v)
def resize(self, *args): def resize(self, *args):
return
Logger.info('MDBody:resize called') Logger.info('MDBody:resize called')
if self.parent: if self.parent:
ps = [0,0,0,0] ps = [0,0,0,0]

View File

@ -2,8 +2,10 @@
from kivy.core.window import Window from kivy.core.window import Window
from kivy.uix.widget import Widget from kivy.uix.widget import Widget
from kivy.app import App from kivy.app import App
from kivy.clock import Clock
from kivy.factory import Factory from kivy.factory import Factory
from .baseWidget import VBox, HBox from kivy.utils import platform
from .baseWidget import VBox, HBox, I18nWidget
from .toggleitems import PressableBox from .toggleitems import PressableBox
from .utils import * from .utils import *
from .swipebehavior import SwipeBehavior from .swipebehavior import SwipeBehavior
@ -56,8 +58,10 @@ in control bar, there is a optional left menu icon, page title, right menu icon,
PagePanel description file format PagePanel description file format
``` ```
{ {
"bar_autohide": true when page is idle
"bar_size": bar size in CSize unit "bar_size": bar size in CSize unit
"bar_at": "top" or "bottom" "bar_at": "top" or "bottom"
"i18n": true of false
"bar_css": "bar_css":
"panel_css": "panel_css":
"left_menu": if defined, it must be a widget instance or a dict "left_menu": if defined, it must be a widget instance or a dict
@ -133,16 +137,24 @@ sub-widget's description file format
""" """
def __init__(self, bar_size=2, def __init__(self, bar_size=2,
bar_css='default', bar_css='default',
i18n=False,
csscls='default', csscls='default',
singlepage=False, singlepage=False,
fixed_before=None, fixed_before=None,
bar_autohide=False,
fixed_after=None, fixed_after=None,
bar_at='top', bar_at='top',
enable_on_close=False, enable_on_close=False,
left_menu=None, **kw): left_menu=None, **kw):
self.bar_size = bar_size self.bar_size = bar_size
self.bar_autohide = bar_autohide
self.bar_at = bar_at self.bar_at = bar_at
self.i18n = i18n
self.singlepage = singlepage self.singlepage = singlepage
self.idle_status = False
self.idle_threshold = 10
self.bar_show = True
self.idle_task = None
self.swipe_buffer = [] self.swipe_buffer = []
self.swipe_right = False self.swipe_right = False
self.fixed_before = None self.fixed_before = None
@ -209,6 +221,10 @@ sub-widget's description file format
}) })
self.bar.add_widget(self.bar_left_menu) self.bar.add_widget(self.bar_left_menu)
self.bar_left_menu.bind(on_press=self.show_left_menu) self.bar_left_menu.bind(on_press=self.show_left_menu)
if self.i18n:
self.i18n_w = I18nWidget(size_hint_x=None, width=CSize(5))
self.bar.add_widget(self.i18n_w)
self.bar_title = HBox(csscls=bar_css) self.bar_title = HBox(csscls=bar_css)
self.bar.add_widget(self.bar_title) self.bar.add_widget(self.bar_title)
self.bar_right_menu = VBox(size_hint=(None,None),size=CSize(bcsize,bcsize)) self.bar_right_menu = VBox(size_hint=(None,None),size=CSize(bcsize,bcsize))
@ -229,15 +245,47 @@ sub-widget's description file format
}) })
self.bar.add_widget(self.bar_right_menu) self.bar.add_widget(self.bar_right_menu)
self.bar_right_menu_w.bind(on_press=self.show_right_menu) self.bar_right_menu_w.bind(on_press=self.show_right_menu)
self.construct()
if self.bar_autohide:
Window.bind(on_touch_down=self.set_normal_bar)
self.idle_task = Clock.schedule_once(self.set_idle_bar, \
self.idle_threshold)
if bar_at == 'top': def set_idle_bar(self, *args):
if not self.bar_show:
return
try:
self.bar_pos = self.children.index(self.bar)
print('self.bar_pos=', self.bar_pos, '......................')
super().remove_widget(self.bar)
if platform in ['win', 'macosx','linux']:
Window.borderless = True
except:
pass
self.bar_show = False
def set_normal_bar(self, *args):
if self.idle_task:
self.idle_task.cancel()
self.idle_task = Clock.schedule_once(self.set_idle_bar, \
self.idle_threshold)
if self.bar_show:
return
super().add_widget(self.bar, index=self.bar_pos)
if platform in ['win', 'macosx','linux']:
Window.borderless = False
self.bar_show = True
def construct(self):
self.clear_widgets()
if self.bar_show and self.bar_at == 'top':
super().add_widget(self.bar) super().add_widget(self.bar)
if self.fixed_before: if self.fixed_before:
super().add_widget(self.fixed_before) super().add_widget(self.fixed_before)
super().add_widget(self.content) super().add_widget(self.content)
if self.fixed_after: if self.fixed_after:
super().add_widget(self.fixed_after) super().add_widget(self.fixed_after)
if bar_at != 'top': if self.bar_show and self.bar_at != 'top':
super().add_widget(self.bar) super().add_widget(self.bar)
self.left_menu_showed = False self.left_menu_showed = False
self.right_menu_showed = False self.right_menu_showed = False

View File

@ -42,10 +42,13 @@ from .block_test import BlockTest
from .hierarchy import Hierarchy from .hierarchy import Hierarchy
from .price import PriceView from .price import PriceView
from .ffpyplayer_video import FFVideo from .ffpyplayer_video import FFVideo
from .upload import UploadFile
r = Factory.register r = Factory.register
# if kivy.platform in ['win','linux', 'macosx']: # if kivy.platform in ['win','linux', 'macosx']:
# r('ScreenWithMic', ScreenWithMic) # r('ScreenWithMic', ScreenWithMic)
r('UploadFile', UploadFile)
r('FFVideo', FFVideo)
r('AnchorBox', AnchorBox) r('AnchorBox', AnchorBox)
r('FloatBox', FloatBox) r('FloatBox', FloatBox)
r('RelativeBox', RelativeBox) r('RelativeBox', RelativeBox)
@ -144,4 +147,3 @@ def register_blocks(name, value):
except: except:
Logger.info(f'plugin : register_blocks():{name} register error') Logger.info(f'plugin : register_blocks():{name} register error')

View File

@ -1,3 +1,5 @@
import os
import traceback
try: try:
import ujson as json import ujson as json
except: except:
@ -9,6 +11,9 @@ from appPublic.myTE import MyTemplateEngine
from appPublic.Singleton import SingletonDecorator from appPublic.Singleton import SingletonDecorator
from appPublic.dictObject import DictObject from appPublic.dictObject import DictObject
from kivy.logger import Logger
from kivy.utils import platform
@SingletonDecorator @SingletonDecorator
class ScriptEnv(DictObject): class ScriptEnv(DictObject):
pass pass
@ -18,8 +23,10 @@ def set_script_env(n,v):
env.update({n:v}) env.update({n:v})
class Script: class Script:
def __init__(self, root): def __init__(self):
self.root = root config = getConfig()
self.root = config.script_root
# print('Script.root=', self.root)
self.env = {} self.env = {}
self.handlers = {} self.handlers = {}
self.register('.tmpl', TemplateHandler) self.register('.tmpl', TemplateHandler)
@ -31,6 +38,20 @@ class Script:
url = url[7:] url = url[7:]
return join(self.root, *url.split('/')) return join(self.root, *url.split('/'))
def show_info(self, env):
workdir = env['workdir']
sfile = env['filepath']
url = env['url']
print(f'script:workdir={workdir}')
print(f'script:script_file={sfile}')
print(f'script:url={url}')
sdir = os.path.join(workdir, 'scripts')
sf_exists = os.path.isdir(sdir)
conf_f = os.path.join(workdir, 'conf', 'config.json')
conf_exists = os.path.isfile(conf_f)
print(f'script:script exists {sf_exists}')
print(f'script:config.json exists {conf_exists}')
def dispatch(self, url, **kw): def dispatch(self, url, **kw):
filepath = self.url2filepath(url) filepath = self.url2filepath(url)
for suffix, handler in self.handlers.items(): for suffix, handler in self.handlers.items():
@ -41,6 +62,10 @@ class Script:
env['root_path'] = self.root env['root_path'] = self.root
env['url'] = url env['url'] = url
env['filepath'] = filepath env['filepath'] = filepath
# print(f"workdir={env['workdir']}--------")
# if platform == 'android':
# self.show_info(env)
h = handler(env) h = handler(env)
d = h.render() d = h.render()
try: try:
@ -90,7 +115,12 @@ class TemplateHandler(BaseHandler):
self.engine = MyTemplateEngine(paths) self.engine = MyTemplateEngine(paths)
def render(self): def render(self):
return self.engine.render(self.templ_file, self.env) try:
return self.engine.render(self.templ_file, self.env)
except Exception as e:
print('Exception:', str(e))
print('filename=', self.env['filepath'])
traceback.print_exc()
class DspyHandler(BaseHandler): class DspyHandler(BaseHandler):
def __init__(self, env): def __init__(self, env):
@ -107,9 +137,15 @@ class DspyHandler(BaseHandler):
return txt return txt
def render(self, params={}): def render(self, params={}):
lenv = self.env.copy() try:
lenv.update(params) lenv = self.env.copy()
txt = self.loadScript(self.env['filepath']) lenv.update(params)
exec(txt,lenv,lenv) txt = self.loadScript(self.env['filepath'])
func = lenv['myfunc'] exec(txt,lenv,lenv)
return func(self.env, **lenv) func = lenv['myfunc']
return func(self.env, **lenv)
except Exception as e:
print('Exception:', str(e))
print('filename=', self.env['filepath'])
traceback.print_exc()

View File

@ -121,18 +121,18 @@ class HttpClient(Http_Client):
self.workers.add(self.webcall,callback,errback,kwargs=kwargs) self.workers.add(self.webcall,callback,errback,kwargs=kwargs)
def get(self, url, params={}, headers={}, callback=None, errback=None): def get(self, url, params={}, headers={}, stream=False, callback=None, errback=None):
return self.__call__(url,method='GET',params=params, return self.__call__(url,method='GET',params=params,
headers=headers, callback=callback, headers=headers, stream=stream, callback=callback,
errback=errback) errback=errback)
def post(self, url, params={}, headers={}, files={}, callback=None, errback=None): def post(self, url, params={}, headers={}, files={}, stream=False, callback=None, errback=None):
return self.__call__(url,method='POST',params=params, files=files, return self.__call__(url,method='POST',params=params, files=files,
headers=headers, callback=callback, headers=headers, stream=stream, callback=callback,
errback=errback) errback=errback)
def put(self, url, params={}, headers={}, callback=None, errback=None): def put(self, url, params={}, headers={}, stream=False, callback=None, errback=None):
return self.__call__(url,method='PUT',params=params, return self.__call__(url,method='PUT',params=params,
headers=headers, callback=callback, headers=headers, stream=stream, callback=callback,
errback=errback) errback=errback)
def delete(self, url, params={}, headers={}, callback=None, errback=None): def delete(self, url, params={}, headers={}, callback=None, errback=None):

View File

@ -1,6 +1,5 @@
from functools import partial from functools import partial
from kivy.logger import Logger from kivy.logger import Logger
from kivy.uix.behaviors import TouchRippleButtonBehavior
from kivy.graphics import Color, Rectangle from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory from kivy.factory import Factory
@ -11,46 +10,7 @@ from kivyblocks.bgcolorbehavior import BGColorBehavior
from kivyblocks.utils import CSize from kivyblocks.utils import CSize
from kivyblocks.baseWidget import Box from kivyblocks.baseWidget import Box
from kivyblocks.widget_css import WidgetCSS from kivyblocks.widget_css import WidgetCSS
from kivyblocks.clickable import PressableBox
class PressableBox(TouchRippleButtonBehavior, Box):
normal_css = StringProperty("default")
actived_css = StringProperty("default")
box_actived = BooleanProperty(False)
def __init__(self,
border_width=1,
user_data=None,
radius=[],
**kw):
super(PressableBox, self).__init__(
padding=[border_width,
border_width,
border_width,
border_width],
radius=radius,
**kw)
self.border_width = border_width
self.user_data = user_data
self.actived = False
self.csscls = self.normal_css
def active(self, flag):
self.box_actived = flag
def on_box_actived(self, o, v):
if self.box_actived:
self.csscls = self.actived_css
else:
self.csscls = self.normal_css
def on_press(self,o=None):
self.box_actived = True
def setValue(self,d):
self.user_data = d
def getValue(self):
return self.user_data
""" """
ToggleItems format: ToggleItems format:

View File

@ -67,16 +67,22 @@ class Toolbar(ScrollPanel):
self.register_event_type('on_delete_tool') self.register_event_type('on_delete_tool')
if self.toolbar_orient == 'H': if self.toolbar_orient == 'H':
self._inner.orientation = 'horizontal' self._inner.orientation = 'horizontal'
self.size_hint_x = 1
else: else:
self._inner.orientation = 'vertical' self._inner.orientation = 'vertical'
self.size_hint_y = 1
self.clear_widgets() self.clear_widgets()
for t in self.tools: for t in self.tools:
self.add_tool(t) self.add_tool(t)
self.bar_width = 0 self.bar_width = 0
if self.toolbar_orient == 'H': if self.toolbar_orient == 'H':
self.size_hint_y = None
self.height = self._inner.height * 1.6
self.do_scroll_y = False self.do_scroll_y = False
else: else:
self.size_hint_x = None
self.width = self._inner.width * 1.6
self.do_scroll_x = False self.do_scroll_x = False
def on_children_size(self, o, size): def on_children_size(self, o, size):

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

57
kivyblocks/upload.py Normal file
View File

@ -0,0 +1,57 @@
import os
from kivy.properties import StringProperty, ListProperty
from kivyblocks.utils import blockImage, CSize
import requests
from plyer import filechooser
from .i18n import I18n
from .clickable import ClickableIconText
from .baseWidget import Running
from .message import Error
class UploadFile(ClickableIconText):
upload_url = StringProperty(None)
name = StringProperty('upfile')
# suffixes = ListProperty([])
def __init__(self, **kw):
super().__init__(**kw)
self.otext = 'please select file'
self.img_kw = {
"size_hint":[None, None],
"width":CSize(1),
"height":CSize(1)
}
self.source = blockImage('upload.png')
self.file_url = None
self.running = None
def getValue(self):
return {
self.name:self.file_url
}
def on_press(self, *args):
i18n = I18n()
filechooser.open_file(title=i18n('open file'),
on_selection=self.file_selected)
def file_selected(self, files):
running = Running(self)
def readfile(f):
with open(f, 'rb') as o:
d = o.read(102400)
if not d:
return
yield d
fpath = files[0]
fn = os.path.basename(fpath)
print('fn=', fn)
headers={
'Content-Type': 'application/octet-stream',
'Content-Disposition':'attachments;filename={}'.format(fn)
}
r = requests.post(self.upload_url,
data=readfile(files[0]),
headers=headers
)
running.dismiss()

View File

@ -1 +1 @@
__version__ = '0.3.0' __version__ = '0.4.1'