bugfix
This commit is contained in:
commit
d65729b431
@ -5,3 +5,5 @@ add script to support local .tmpl and .dspy file translation. it can build dynam
|
||||
|
||||
## version 0.3.1
|
||||
* uses weakref to collect all the i18n widgets in i18n.py
|
||||
* show video play position
|
||||
|
||||
|
0
kivyblocks/anz/__init__.py
Normal file
0
kivyblocks/anz/__init__.py
Normal file
46
kivyblocks/anz/bluetooth.py
Normal file
46
kivyblocks/anz/bluetooth.py
Normal 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]
|
||||
|
@ -2,6 +2,7 @@ import sys
|
||||
import math
|
||||
from traceback import print_exc
|
||||
|
||||
from kivy.resources import resource_find
|
||||
from kivy.properties import ObjectProperty, StringProperty, \
|
||||
NumericProperty, BooleanProperty, OptionProperty
|
||||
from kivy.properties import DictProperty
|
||||
@ -67,7 +68,7 @@ from .widgetExt.inputext import FloatInput,IntegerInput, \
|
||||
StrInput,SelectInput, BoolInput, Password
|
||||
from .widgetExt.messager import Messager
|
||||
from .bgcolorbehavior import BGColorBehavior
|
||||
from .utils import NeedLogin, InsufficientPrivilege, HTTPError
|
||||
from .utils import NeedLogin, InsufficientPrivilege, HTTPError, blockImage
|
||||
from .login import LoginForm
|
||||
from .tab import TabsPanel
|
||||
from .threadcall import HttpClient
|
||||
@ -76,6 +77,27 @@ from .widget_css import WidgetCSS
|
||||
from .ready import WidgetReady
|
||||
from .utils import CSize, SUPER
|
||||
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':
|
||||
from .widgetExt.phonebutton import PhoneButton
|
||||
@ -145,33 +167,23 @@ class Text(Label):
|
||||
def __init__(self,i18n=False, texttype='text', wrap=False,
|
||||
fgcolor=None, **kw):
|
||||
|
||||
fontsize={'font_size':CSize(1)}
|
||||
offset={
|
||||
'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)}
|
||||
fontsize = font_sizes.get(texttype)
|
||||
fontname = font_names.get(texttype)
|
||||
self._i18n = i18n
|
||||
self.i18n = I18n()
|
||||
self.bgcolor = fgcolor
|
||||
kwargs = kw.copy()
|
||||
config = getConfig()
|
||||
self.wrap = wrap
|
||||
if kwargs.get('font_size') and texttype=='text':
|
||||
pass
|
||||
else:
|
||||
kwargs.update(fontsize)
|
||||
kwargs.update({
|
||||
'font_size':fontsize,
|
||||
'font_name':fontname
|
||||
})
|
||||
if not kwargs.get('text'):
|
||||
kwargs['text'] = kwargs.get('otext','')
|
||||
|
||||
SUPER(Text, self, kwargs)
|
||||
if self._i18n:
|
||||
self.i18n = I18n()
|
||||
self.i18n.addI18nWidget(self)
|
||||
if self.wrap:
|
||||
self.size_hint_y = None
|
||||
@ -220,6 +232,7 @@ class Text(Label):
|
||||
self.lang = lang
|
||||
|
||||
def on_lang(self,o,lang):
|
||||
if self._i18n and self.otext:
|
||||
self.text = self.i18n(self.otext)
|
||||
|
||||
class Title1(Text):
|
||||
@ -256,12 +269,19 @@ class Modal(VBox):
|
||||
content = DictProperty(None)
|
||||
auto_open = 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',
|
||||
'bl', 'bc', 'br'])
|
||||
|
||||
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:
|
||||
blocks = Factory.Blocks()
|
||||
self.content_w = blocks.widgetBuild(self.content)
|
||||
@ -269,10 +289,6 @@ class Modal(VBox):
|
||||
self.add_widget(self.content_w)
|
||||
else:
|
||||
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):
|
||||
if not self.collide_point(touch.x, touch.y):
|
||||
@ -283,41 +299,74 @@ class Modal(VBox):
|
||||
|
||||
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]
|
||||
yn = self.position[0]
|
||||
x, y = 0, 0
|
||||
if xn == 'c':
|
||||
x = (w.width - self.width) / 2
|
||||
x = (self._target.width - self.width) / 2
|
||||
elif xn == 'r':
|
||||
x = w.width - self.width
|
||||
x = self._target.width - self.width
|
||||
if x < 0:
|
||||
x = 0
|
||||
if yn == 'c':
|
||||
y = (w.height - self.height) / 2
|
||||
elif yn == 'b':
|
||||
y = w.height - self.height
|
||||
y = (self._target.height - self.height) / 2
|
||||
elif yn == 't':
|
||||
y = self._target.height - self.height
|
||||
if y < 0:
|
||||
y = 0
|
||||
if w == Window:
|
||||
if self._target == Window:
|
||||
self.pos = x, y
|
||||
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:
|
||||
return
|
||||
self.parent.remove_widget(self)
|
||||
self.dispatch('on_pre_open')
|
||||
if widget is None:
|
||||
widget = Window
|
||||
self.set_modal_position(widget)
|
||||
Window.add_widget(self)
|
||||
self.dispatch('on_open')
|
||||
if self._target != Window:
|
||||
self._target.disabled = True
|
||||
|
||||
def dismiss(self, *args):
|
||||
self.dispatch('on_pre_dismiss')
|
||||
self.dispatch('on_dismiss')
|
||||
Window.remove_widget(self)
|
||||
if self._target != Window:
|
||||
self._target.enabled = False
|
||||
|
||||
def on_open(self, *args):
|
||||
pass
|
||||
@ -333,7 +382,6 @@ class Modal(VBox):
|
||||
|
||||
def add_widget(self, w, *args, **kw):
|
||||
super().add_widget(w, *args, **kw)
|
||||
# super().add_widget(Label(text='1111'))
|
||||
if self.auto_open:
|
||||
self.open()
|
||||
|
||||
@ -358,6 +406,32 @@ class TimedModal(Modal):
|
||||
self.time_task = None
|
||||
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):
|
||||
def on_press(self):
|
||||
pass
|
||||
@ -366,6 +440,10 @@ class PressableLabel(ButtonBehavior, Text):
|
||||
def on_press(self):
|
||||
pass
|
||||
|
||||
class PressableText(ButtonBehavior, Text):
|
||||
def on_press(self):
|
||||
pass
|
||||
|
||||
class FILEDataHandler(EventDispatcher):
|
||||
def __init__(self, url, suffixs=[],params={}):
|
||||
self.url = url
|
||||
@ -566,3 +644,29 @@ class Slider(Carousel):
|
||||
w = bk.widgetBuild(desc)
|
||||
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)
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import os
|
||||
import sys
|
||||
import codecs
|
||||
@ -818,3 +819,822 @@ class {{ classname }}({% for b in bases -%}{{b}}{% endfor %})
|
||||
Factory.register('Blocks',Blocks)
|
||||
Factory.register('Video',Video)
|
||||
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
|
||||
|
@ -2,12 +2,6 @@
|
||||
import os
|
||||
import sys
|
||||
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 codecs
|
||||
import json
|
||||
@ -23,6 +17,7 @@ from kivy.core.window import WindowBase, Window
|
||||
from kivy.clock import Clock
|
||||
from kivy.logger import Logger
|
||||
from kivy.utils import platform
|
||||
from kivy.metrics import Metrics
|
||||
from kivy.app import App
|
||||
|
||||
import plyer
|
||||
@ -31,20 +26,25 @@ from .register import *
|
||||
from .threadcall import HttpClient,Workers
|
||||
from .utils import *
|
||||
from .widget_css import register_css
|
||||
from .version import __version__
|
||||
|
||||
if platform == 'android':
|
||||
from android.storage import app_storage_path
|
||||
from jnius import autoclass
|
||||
|
||||
from .android_rotation import get_rotation
|
||||
|
||||
Logger.info(f'KivyBlocks:version={__version__}')
|
||||
def signal_handler(signal, frame):
|
||||
app = App.get_running_app()
|
||||
if app is None:
|
||||
return
|
||||
app.workers.running = False
|
||||
app.stop()
|
||||
print('Singal handled .........')
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
|
||||
class BlocksApp(App):
|
||||
def get_rotation(self):
|
||||
return get_rotation()
|
||||
@ -80,6 +80,8 @@ class BlocksApp(App):
|
||||
self.public_headers = {
|
||||
"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_rotate=self.on_rotate)
|
||||
Window.bind(size=self.device_info)
|
||||
@ -104,23 +106,19 @@ class BlocksApp(App):
|
||||
|
||||
def get_user_data_path(self):
|
||||
if platform == 'android':
|
||||
Environment = autoclass('android.os.Environment')
|
||||
sdpath = Environment.getExternalStorageDirectory()
|
||||
return str(sdpath)
|
||||
# Environment = autoclass('android.os.Environment')
|
||||
# sdpath = Environment.getExternalStorageDirectory()
|
||||
# return str(sdpath)
|
||||
return str(app_storage_path())
|
||||
sdpath = App.get_running_app().user_data_dir
|
||||
return str(sdpath)
|
||||
|
||||
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)
|
||||
return fname
|
||||
|
||||
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):
|
||||
def default_profile(self):
|
||||
device_id = getID()
|
||||
try:
|
||||
device_id = plyer.uniqueid.id
|
||||
@ -132,6 +130,15 @@ class BlocksApp(App):
|
||||
d = {
|
||||
'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)
|
||||
|
||||
def read_profile(self):
|
||||
|
@ -16,6 +16,11 @@ from kivyblocks.widget_css import WidgetCSS
|
||||
from .uitype.factory import UiFactory, get_value
|
||||
from .command_action import cmd_action
|
||||
|
||||
def on_disabled(self, *args):
|
||||
pass
|
||||
|
||||
TouchRippleButtonBehavior.on_disabled = on_disabled
|
||||
|
||||
class TinyText(Text):
|
||||
def __init__(self, **kw):
|
||||
SUPER(TinyText, self, kw)
|
||||
@ -26,6 +31,46 @@ class TinyText(Text):
|
||||
self.texture_update()
|
||||
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):
|
||||
def __init__(self,
|
||||
border_width=1,
|
||||
|
@ -15,7 +15,6 @@ def get_cmd_desc(cmd_desc):
|
||||
desc['datamethod'] = v['datamethod']
|
||||
keys = v.keys()
|
||||
if 'url' in keys:
|
||||
Logger.info('get_cmd_desc():cmd_desc=%s', cmd_desc)
|
||||
desc['actiontype'] = 'urlwidget'
|
||||
desc['mode'] = 'replace'
|
||||
options = {
|
||||
@ -24,18 +23,22 @@ def get_cmd_desc(cmd_desc):
|
||||
}
|
||||
desc['options'] = options
|
||||
return desc
|
||||
|
||||
if 'rfname' in keys:
|
||||
desc['actiontype'] = 'registedfunction'
|
||||
desc['params'] = v.get('params',{})
|
||||
return desc
|
||||
|
||||
if 'script' in keys:
|
||||
desc['actiontype'] = 'script'
|
||||
desc['script'] = v.get('script')
|
||||
return desc
|
||||
|
||||
if 'method' in keys:
|
||||
desc['actiontype'] = 'method'
|
||||
desc['method'] = v.get('method')
|
||||
return desc
|
||||
|
||||
if 'actions' in keys:
|
||||
desc['actiontype'] = 'multiple'
|
||||
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.open()
|
||||
Logger.info('cmd_action():desc=%s, conform and action', desc)
|
||||
print('cmd_action():desc=', desc, ' conform and action', desc)
|
||||
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
|
||||
from kivy.event import EventDispatcher
|
||||
from kivy.app import App
|
||||
@ -191,3 +192,187 @@ class RegisterFunctionDataLoader(DataLoader):
|
||||
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
|
||||
|
@ -25,7 +25,7 @@ from appPublic.uniqueID import getID
|
||||
from appPublic.myTE import string_template_render
|
||||
|
||||
from .utils import *
|
||||
from .baseWidget import Text, HBox, VBox
|
||||
from .baseWidget import Text, HBox, VBox, Running
|
||||
from .scrollpanel import ScrollPanel
|
||||
from .paging import Paging, RelatedLoader
|
||||
from .ready import WidgetReady
|
||||
@ -64,7 +64,7 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout):
|
||||
if self.row.header:
|
||||
self.csscls=self.row.part.datagrid.header_css
|
||||
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,
|
||||
halign='left', valign='middle'
|
||||
)
|
||||
@ -432,7 +432,7 @@ class DataGrid(VBox):
|
||||
desc = {
|
||||
"widgettype":"Text",
|
||||
"options":{
|
||||
"text":n,
|
||||
"otext":n,
|
||||
"i18n":True,
|
||||
}
|
||||
}
|
||||
|
7
kivyblocks/download.py
Normal file
7
kivyblocks/download.py
Normal file
@ -0,0 +1,7 @@
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
class DownloadFile(ClickableIconText):
|
||||
def __init__(self, **kw):
|
||||
pass
|
||||
|
||||
|
@ -6,12 +6,14 @@ from ffpyplayer.tools import set_log_callback
|
||||
|
||||
from kivy.factory import Factory
|
||||
from kivy.app import App
|
||||
from kivy.core.window import Window
|
||||
from kivy.uix.image import Image
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import StringProperty, BooleanProperty, \
|
||||
OptionProperty, NumericProperty
|
||||
from kivy.graphics.texture import Texture
|
||||
from kivy.graphics import Color, Line
|
||||
from kivyblocks.ready import WidgetReady
|
||||
|
||||
|
||||
@ -26,17 +28,17 @@ class FFVideo(WidgetReady, Image):
|
||||
duration = NumericProperty(-1)
|
||||
position = NumericProperty(-1)
|
||||
volume = NumericProperty(-1)
|
||||
framerate = NumericProperty(180)
|
||||
in_center_focus = BooleanProperty(False)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._player = None
|
||||
self._update_task = None
|
||||
self._texture = None
|
||||
self.vh_task = None
|
||||
self.is_black = False
|
||||
self.videosize = None
|
||||
self.timeperiod = 1.0 / self.framerate
|
||||
self.ff_opts = {}
|
||||
self.ff_opts = {
|
||||
"framedrop":True
|
||||
}
|
||||
self.lib_opts = {}
|
||||
self.headers_pattern = {}
|
||||
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_enter_focus')
|
||||
self.register_event_type('on_load_success')
|
||||
self.register_event_type('on_startplay')
|
||||
|
||||
def on_open_failed(self, *args):
|
||||
pass
|
||||
@ -101,10 +104,14 @@ class FFVideo(WidgetReady, Image):
|
||||
def on_status(self, *args):
|
||||
print('on_status called, ', self.status)
|
||||
if self._player is None:
|
||||
print('no _player, do nothing')
|
||||
return
|
||||
|
||||
Window.allow_screensaver = True
|
||||
if self.status == 'play':
|
||||
Window.allow_screensaver = False
|
||||
self._player.set_pause(False)
|
||||
self.video_handle()
|
||||
elif self.status == 'pause':
|
||||
self._player.set_pause(True)
|
||||
elif self.status == 'stop':
|
||||
@ -112,20 +119,35 @@ class FFVideo(WidgetReady, Image):
|
||||
else:
|
||||
pass
|
||||
|
||||
def on_parent(self, *args):
|
||||
if self.parent:
|
||||
return
|
||||
self._play_stop()
|
||||
|
||||
def on_startplay(self, *args):
|
||||
pass
|
||||
|
||||
def on_frame(self, *args):
|
||||
if self._player is None:
|
||||
return
|
||||
if self.audio_id is None:
|
||||
return
|
||||
self._player.request_channel(self, 'audio', 'open', self.audio_id)
|
||||
if not self.playing:
|
||||
self.dispatch('on_startplay')
|
||||
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):
|
||||
if self._update_task:
|
||||
self._update_task.cancel()
|
||||
self._update_task = None
|
||||
if self._player:
|
||||
self._player.close_player()
|
||||
self._player = None
|
||||
self._play_stop()
|
||||
|
||||
def set_volume(self, v):
|
||||
if self.play_mode == 'preview':
|
||||
@ -141,23 +163,27 @@ class FFVideo(WidgetReady, Image):
|
||||
self.volume = v
|
||||
|
||||
def seek(self, pts):
|
||||
self.vsync = False
|
||||
if self.play_mode == 'preview':
|
||||
return
|
||||
if self._player is None:
|
||||
return
|
||||
if self.status != 'play':
|
||||
return
|
||||
self._player.seek(pts)
|
||||
self._player.seek(pts, relative=False)
|
||||
self.position = self._player.get_pts()
|
||||
self.last_val = None
|
||||
|
||||
def mute(self, flag):
|
||||
def mute(self, flag=None):
|
||||
if self.play_mode == 'preview':
|
||||
return
|
||||
if self._player is None:
|
||||
return
|
||||
if self.status != 'play':
|
||||
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):
|
||||
if self.play_mode == 'preview':
|
||||
@ -169,7 +195,9 @@ class FFVideo(WidgetReady, Image):
|
||||
self._player.request_channel('audio', action='cycle')
|
||||
|
||||
def on_v_src(self, o, src):
|
||||
self._play_stop()
|
||||
self.status = 'stop'
|
||||
self.playing = False
|
||||
|
||||
ff_opts = {
|
||||
'pause':False
|
||||
}
|
||||
@ -194,7 +222,8 @@ class FFVideo(WidgetReady, Image):
|
||||
# self._player = MediaPlayer(self.v_src)
|
||||
self._player = MediaPlayer(self.v_src, ff_opts=ff_opts, \
|
||||
lib_opts=lib_opts)
|
||||
self._play_start()
|
||||
# self._play_start()
|
||||
self.status = 'play'
|
||||
|
||||
def file_opened(self, files):
|
||||
self.v_src = files[0]
|
||||
@ -202,7 +231,6 @@ class FFVideo(WidgetReady, Image):
|
||||
def play(self):
|
||||
if self._player is None:
|
||||
return
|
||||
# self._player.set_pause(False)
|
||||
self.status = 'play'
|
||||
|
||||
def pause(self):
|
||||
@ -212,33 +240,32 @@ class FFVideo(WidgetReady, Image):
|
||||
self.status = 'pause'
|
||||
|
||||
def _play_start(self):
|
||||
self.timepass = 0.0
|
||||
self.last_frame = None
|
||||
self.is_black = False
|
||||
self.first_play = True
|
||||
self._update_task = Clock.schedule_interval(self._update, self.timeperiod)
|
||||
self.vsync = False
|
||||
|
||||
def _get_video_info(self):
|
||||
if self.first_play:
|
||||
if not self.playing:
|
||||
self.playing = True
|
||||
meta = self._player.get_metadata()
|
||||
self.duration = meta['duration']
|
||||
self.position = 0
|
||||
self._out_fmt = meta['src_pix_fmt']
|
||||
self.frame_rate = meta['frame_rate']
|
||||
self.videosize = meta['src_vid_size']
|
||||
|
||||
def _play_stop(self):
|
||||
if self._player is None:
|
||||
return
|
||||
self._update_task.cancel()
|
||||
self._update_task = None
|
||||
if self._player:
|
||||
self._player.close_player()
|
||||
self._player = None
|
||||
self.timepass = 0
|
||||
if self._update_task:
|
||||
self._update_task.cancel()
|
||||
self._update_task = None
|
||||
if self.vh_task:
|
||||
self.vh_task.cancel()
|
||||
self.vh_task = None
|
||||
self.next_frame = None
|
||||
self.duration = -1
|
||||
self.position = -1
|
||||
self.frame_rate = None
|
||||
self.position = 0
|
||||
self.videosize = None
|
||||
|
||||
def on_size(self, *args):
|
||||
@ -283,7 +310,6 @@ class FFVideo(WidgetReady, Image):
|
||||
fbo['tex_y'] = 0
|
||||
fbo['tex_u'] = 1
|
||||
fbo['tex_v'] = 2
|
||||
self._texture = fbo.texture
|
||||
dy, du, dv, _ = img.to_memoryview()
|
||||
if dy and du and dv:
|
||||
self._tex_y.blit_buffer(dy, colorfmt='luminance')
|
||||
@ -304,35 +330,44 @@ class FFVideo(WidgetReady, Image):
|
||||
self.texture = texture
|
||||
# print('img_size=', w, h, 'window size=', self.size)
|
||||
|
||||
def _update(self, dt):
|
||||
if self.last_frame is None:
|
||||
def video_handle(self, *args):
|
||||
if self._update_task:
|
||||
self._update_task.cancel()
|
||||
self._update_task = None
|
||||
if self._player is None:
|
||||
return
|
||||
frame, val = self._player.get_frame()
|
||||
if val == 'eof':
|
||||
print('*****EOF******')
|
||||
self.status = 'stop'
|
||||
self.set_black()
|
||||
self.last_val = None
|
||||
return
|
||||
if val == 'pause':
|
||||
self.status = 'pause'
|
||||
self.last_val = None
|
||||
return
|
||||
if frame is None:
|
||||
# print('video null', time.time())
|
||||
self.set_black()
|
||||
self.last_val = None
|
||||
self.vh_task = Clock.schedule_once(self.video_handle, 0.1)
|
||||
return
|
||||
|
||||
self. _get_video_info()
|
||||
self.last_frame = frame
|
||||
self.video_ts = val
|
||||
self._get_video_info()
|
||||
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)
|
||||
|
||||
self.timepass += self.timeperiod
|
||||
def do_update(self, *args):
|
||||
self.position = self._player.get_pts()
|
||||
if self.timepass < self.video_ts:
|
||||
return
|
||||
|
||||
if self.timepass > self.video_ts +0.2:
|
||||
self.last_frame = None
|
||||
return
|
||||
|
||||
self.status = 'play'
|
||||
self.volume = self._player.get_volume()
|
||||
img, t = self.last_frame
|
||||
if self._out_fmt == 'yuv420p':
|
||||
self.show_yuv420(img)
|
||||
@ -340,5 +375,5 @@ class FFVideo(WidgetReady, Image):
|
||||
self.show_others(img)
|
||||
self.dispatch('on_frame', self.last_frame)
|
||||
self.last_frame = None
|
||||
self.vh_task = Clock.schedule_once(self.video_handle, 0)
|
||||
|
||||
Factory.register('FFVideo', FFVideo)
|
||||
|
@ -13,31 +13,34 @@ from .scrollpanel import ScrollPanel
|
||||
from .clickable import SingleCheckBox
|
||||
from .baseWidget import Text
|
||||
from .utils import CSize
|
||||
from .command_action import cmd_action
|
||||
|
||||
from appPublic.registerfunction import getRegisterFunctionByName
|
||||
|
||||
class TreeViewComplexNode(BoxLayout, TreeViewLabel):
|
||||
class TreeViewComplexNode(BoxLayout, TreeViewNode):
|
||||
otext = StringProperty(None)
|
||||
font_size_c = NumericProperty(1)
|
||||
node_height = NumericProperty(2)
|
||||
checkbox = BooleanProperty(False)
|
||||
icon = StringProperty(None)
|
||||
data = DictProperty(None)
|
||||
def __init__(self, **kw):
|
||||
super(TreeViewComplexNode, self).__init__(**kw)
|
||||
self.orientation = 'horizontal'
|
||||
self.size_hint_x = None
|
||||
if self.checkbox:
|
||||
cb = SingleCheckBox(size_hint=(None,None))
|
||||
cb.bind(on_press=self.set_checked)
|
||||
self.add_widget(cb)
|
||||
if self.icon:
|
||||
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)
|
||||
txt = Text(otext=self.otext, i18n=True)
|
||||
txt.texture_update()
|
||||
txt.size_hint = (None, None)
|
||||
txt.size = txt.texture_size
|
||||
txt = Text(otext=self.otext, i18n=True, wrap=True,
|
||||
font_size=CSize(self.font_size_c),halign='left')
|
||||
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):
|
||||
if not self.data:
|
||||
@ -52,6 +55,8 @@ class Hierarchy(ScrollPanel):
|
||||
params = DictProperty(None)
|
||||
method = StringProperty('get')
|
||||
idField = StringProperty(None)
|
||||
node_height = NumericProperty(2)
|
||||
font_size_c = NumericProperty(1)
|
||||
textField = StringProperty(None)
|
||||
data = ListProperty(None)
|
||||
checkbox = BooleanProperty(False)
|
||||
@ -60,13 +65,17 @@ class Hierarchy(ScrollPanel):
|
||||
def __init__(self, **kw):
|
||||
self.register_event_type('on_press')
|
||||
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(selected_node=self.node_selected)
|
||||
super(Hierarchy, self).__init__(inner=self.tree, **kw)
|
||||
if self.url:
|
||||
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):
|
||||
print('selected node=', node)
|
||||
|
||||
@ -95,12 +104,14 @@ class Hierarchy(ScrollPanel):
|
||||
def create_new_node(self, data, node=None):
|
||||
n = TreeViewComplexNode(otext=data[self.textField],
|
||||
checkbox=self.checkbox,
|
||||
node_height=self.node_height,
|
||||
font_size_c=self.font_size_c,
|
||||
icon=data.get('icon') or self.icon
|
||||
)
|
||||
n.data = data
|
||||
n.width = self.tree.indent_start + \
|
||||
self.tree.indent_level * n.level \
|
||||
+ sum([i.width for i in n.children])
|
||||
# n.width = self.tree.indent_start + \
|
||||
# self.tree.indent_level * n.level \
|
||||
# + sum([i.width for i in n.children])
|
||||
if node:
|
||||
self.tree.add_node(n, node)
|
||||
else:
|
||||
@ -148,50 +159,10 @@ class Menu(Hierarchy):
|
||||
def on_press(self, node):
|
||||
self.tree.deselect_node()
|
||||
data = {}
|
||||
dw = node.data.get('datawidget')
|
||||
if dw:
|
||||
data_widget = Factory.Blocks.getWidgetById(dw)
|
||||
if data_widget:
|
||||
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
|
||||
data = node.data.copy()
|
||||
if self.target and data.get('target') is None:
|
||||
data['target'] = self.target
|
||||
return cmd_action(data, self)
|
||||
|
||||
Factory.register('Hierarchy', Hierarchy)
|
||||
Factory.register('Menu', Menu)
|
||||
|
@ -14,6 +14,9 @@ class I18n:
|
||||
def __init__(self):
|
||||
self.kvlang={}
|
||||
self.lang = locale.getdefaultlocale()[0]
|
||||
if self.lang is None:
|
||||
self.lang = 'en_US'
|
||||
self.languages = None
|
||||
self.loadI18n(self.lang)
|
||||
self.i18nWidgets = []
|
||||
|
||||
@ -21,8 +24,9 @@ class I18n:
|
||||
self.i18nWidgets.append(ref(w))
|
||||
|
||||
def loadI18nFromI18nFolder(self, lang):
|
||||
config = gtConfig()
|
||||
config = getConfig()
|
||||
fpath = os.path.join(config.i18n_folder,lang,'msg.txt')
|
||||
print('fpath=', fpath, type(fpath))
|
||||
with codecs.open(fpath,'r','utf-8') as f:
|
||||
line = f.readline()
|
||||
d = {}
|
||||
@ -32,15 +36,39 @@ class I18n:
|
||||
continue
|
||||
k,v = line.split(':',1)
|
||||
d.update({k:v})
|
||||
line = readline()
|
||||
line = f.readline()
|
||||
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):
|
||||
app = App.get_running_app()
|
||||
config = getConfig()
|
||||
self.kvlang[lang] = {}
|
||||
if config.i18n_folder:
|
||||
self.kvlang[lang] = self.loadI18nFromFolder(lang)
|
||||
self.kvlang[lang] = self.loadI18nFromI18nFolder(lang)
|
||||
return
|
||||
|
||||
if config.i18n_url:
|
||||
@ -63,8 +91,8 @@ class I18n:
|
||||
if not d:
|
||||
self.loadI18n(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
|
||||
for w in self.i18nWidgets:
|
||||
w.changeLang(lang)
|
||||
|
||||
|
BIN
kivyblocks/imgs/download.png
Normal file
BIN
kivyblocks/imgs/download.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
kivyblocks/imgs/upload.png
Normal file
BIN
kivyblocks/imgs/upload.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
@ -191,7 +191,7 @@ class MarkdownBody(VBox):
|
||||
self.md_obj = md_obj
|
||||
super().__init__(**kw)
|
||||
self.padding=padding
|
||||
self.size_hint = None,None
|
||||
self.size_hint = 1,None
|
||||
self.bind(parent=self.resize)
|
||||
self.resize()
|
||||
|
||||
@ -205,6 +205,7 @@ class MarkdownBody(VBox):
|
||||
f(v)
|
||||
|
||||
def resize(self, *args):
|
||||
return
|
||||
Logger.info('MDBody:resize called')
|
||||
if self.parent:
|
||||
ps = [0,0,0,0]
|
||||
|
@ -2,8 +2,10 @@
|
||||
from kivy.core.window import Window
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.app import App
|
||||
from kivy.clock import Clock
|
||||
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 .utils import *
|
||||
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
|
||||
```
|
||||
{
|
||||
"bar_autohide": true when page is idle
|
||||
"bar_size": bar size in CSize unit
|
||||
"bar_at": "top" or "bottom"
|
||||
"i18n": true of false
|
||||
"bar_css":
|
||||
"panel_css":
|
||||
"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,
|
||||
bar_css='default',
|
||||
i18n=False,
|
||||
csscls='default',
|
||||
singlepage=False,
|
||||
fixed_before=None,
|
||||
bar_autohide=False,
|
||||
fixed_after=None,
|
||||
bar_at='top',
|
||||
enable_on_close=False,
|
||||
left_menu=None, **kw):
|
||||
self.bar_size = bar_size
|
||||
self.bar_autohide = bar_autohide
|
||||
self.bar_at = bar_at
|
||||
self.i18n = i18n
|
||||
self.singlepage = singlepage
|
||||
self.idle_status = False
|
||||
self.idle_threshold = 10
|
||||
self.bar_show = True
|
||||
self.idle_task = None
|
||||
self.swipe_buffer = []
|
||||
self.swipe_right = False
|
||||
self.fixed_before = None
|
||||
@ -209,6 +221,10 @@ sub-widget's description file format
|
||||
})
|
||||
self.bar.add_widget(self.bar_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.add_widget(self.bar_title)
|
||||
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_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)
|
||||
if self.fixed_before:
|
||||
super().add_widget(self.fixed_before)
|
||||
super().add_widget(self.content)
|
||||
if 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)
|
||||
self.left_menu_showed = False
|
||||
self.right_menu_showed = False
|
||||
|
@ -42,10 +42,13 @@ from .block_test import BlockTest
|
||||
from .hierarchy import Hierarchy
|
||||
from .price import PriceView
|
||||
from .ffpyplayer_video import FFVideo
|
||||
from .upload import UploadFile
|
||||
|
||||
r = Factory.register
|
||||
# if kivy.platform in ['win','linux', 'macosx']:
|
||||
# r('ScreenWithMic', ScreenWithMic)
|
||||
r('UploadFile', UploadFile)
|
||||
r('FFVideo', FFVideo)
|
||||
r('AnchorBox', AnchorBox)
|
||||
r('FloatBox', FloatBox)
|
||||
r('RelativeBox', RelativeBox)
|
||||
@ -144,4 +147,3 @@ def register_blocks(name, value):
|
||||
except:
|
||||
Logger.info(f'plugin : register_blocks():{name} register error')
|
||||
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import os
|
||||
import traceback
|
||||
try:
|
||||
import ujson as json
|
||||
except:
|
||||
@ -9,6 +11,9 @@ from appPublic.myTE import MyTemplateEngine
|
||||
from appPublic.Singleton import SingletonDecorator
|
||||
from appPublic.dictObject import DictObject
|
||||
|
||||
from kivy.logger import Logger
|
||||
from kivy.utils import platform
|
||||
|
||||
@SingletonDecorator
|
||||
class ScriptEnv(DictObject):
|
||||
pass
|
||||
@ -18,8 +23,10 @@ def set_script_env(n,v):
|
||||
env.update({n:v})
|
||||
|
||||
class Script:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
def __init__(self):
|
||||
config = getConfig()
|
||||
self.root = config.script_root
|
||||
# print('Script.root=', self.root)
|
||||
self.env = {}
|
||||
self.handlers = {}
|
||||
self.register('.tmpl', TemplateHandler)
|
||||
@ -31,6 +38,20 @@ class Script:
|
||||
url = url[7:]
|
||||
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):
|
||||
filepath = self.url2filepath(url)
|
||||
for suffix, handler in self.handlers.items():
|
||||
@ -41,6 +62,10 @@ class Script:
|
||||
env['root_path'] = self.root
|
||||
env['url'] = url
|
||||
env['filepath'] = filepath
|
||||
# print(f"workdir={env['workdir']}--------")
|
||||
# if platform == 'android':
|
||||
# self.show_info(env)
|
||||
|
||||
h = handler(env)
|
||||
d = h.render()
|
||||
try:
|
||||
@ -90,7 +115,12 @@ class TemplateHandler(BaseHandler):
|
||||
self.engine = MyTemplateEngine(paths)
|
||||
|
||||
def render(self):
|
||||
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):
|
||||
def __init__(self, env):
|
||||
@ -107,9 +137,15 @@ class DspyHandler(BaseHandler):
|
||||
return txt
|
||||
|
||||
def render(self, params={}):
|
||||
try:
|
||||
lenv = self.env.copy()
|
||||
lenv.update(params)
|
||||
txt = self.loadScript(self.env['filepath'])
|
||||
exec(txt,lenv,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()
|
||||
|
||||
|
@ -121,18 +121,18 @@ class HttpClient(Http_Client):
|
||||
|
||||
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,
|
||||
headers=headers, callback=callback,
|
||||
headers=headers, stream=stream, callback=callback,
|
||||
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,
|
||||
headers=headers, callback=callback,
|
||||
headers=headers, stream=stream, callback=callback,
|
||||
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,
|
||||
headers=headers, callback=callback,
|
||||
headers=headers, stream=stream, callback=callback,
|
||||
errback=errback)
|
||||
|
||||
def delete(self, url, params={}, headers={}, callback=None, errback=None):
|
||||
|
@ -1,6 +1,5 @@
|
||||
from functools import partial
|
||||
from kivy.logger import Logger
|
||||
from kivy.uix.behaviors import TouchRippleButtonBehavior
|
||||
from kivy.graphics import Color, Rectangle
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.factory import Factory
|
||||
@ -11,46 +10,7 @@ from kivyblocks.bgcolorbehavior import BGColorBehavior
|
||||
from kivyblocks.utils import CSize
|
||||
from kivyblocks.baseWidget import Box
|
||||
from kivyblocks.widget_css import WidgetCSS
|
||||
|
||||
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
|
||||
from kivyblocks.clickable import PressableBox
|
||||
|
||||
"""
|
||||
ToggleItems format:
|
||||
|
@ -67,16 +67,22 @@ class Toolbar(ScrollPanel):
|
||||
self.register_event_type('on_delete_tool')
|
||||
if self.toolbar_orient == 'H':
|
||||
self._inner.orientation = 'horizontal'
|
||||
self.size_hint_x = 1
|
||||
else:
|
||||
self._inner.orientation = 'vertical'
|
||||
self.size_hint_y = 1
|
||||
self.clear_widgets()
|
||||
for t in self.tools:
|
||||
self.add_tool(t)
|
||||
|
||||
self.bar_width = 0
|
||||
if self.toolbar_orient == 'H':
|
||||
self.size_hint_y = None
|
||||
self.height = self._inner.height * 1.6
|
||||
self.do_scroll_y = False
|
||||
else:
|
||||
self.size_hint_x = None
|
||||
self.width = self._inner.width * 1.6
|
||||
self.do_scroll_x = False
|
||||
|
||||
def on_children_size(self, o, size):
|
||||
|
BIN
kivyblocks/ttf/Alimama_ShuHeiTi_Bold.ttf
Normal file
BIN
kivyblocks/ttf/Alimama_ShuHeiTi_Bold.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/ttf/TsangerYuYangT_W01_W01.ttf
Normal file
BIN
kivyblocks/ttf/TsangerYuYangT_W01_W01.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/ttf/TsangerYuYangT_W02_W02.ttf
Normal file
BIN
kivyblocks/ttf/TsangerYuYangT_W02_W02.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/ttf/TsangerYuYangT_W03_W03.ttf
Normal file
BIN
kivyblocks/ttf/TsangerYuYangT_W03_W03.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/ttf/TsangerYuYangT_W04_W04.ttf
Normal file
BIN
kivyblocks/ttf/TsangerYuYangT_W04_W04.ttf
Normal file
Binary file not shown.
BIN
kivyblocks/ttf/TsangerYuYangT_W05_W05.ttf
Normal file
BIN
kivyblocks/ttf/TsangerYuYangT_W05_W05.ttf
Normal file
Binary file not shown.
57
kivyblocks/upload.py
Normal file
57
kivyblocks/upload.py
Normal 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()
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '0.3.0'
|
||||
__version__ = '0.4.1'
|
||||
|
Loading…
Reference in New Issue
Block a user