Compare commits

..

No commits in common. "9e2b5db1a634b28fa8ea63cee917c96e4b7ea3ef" and "1c17b6165c5dd7b6bb992e816d07f897c4880e11" have entirely different histories.

254 changed files with 1516 additions and 1306 deletions

0
README.md Executable file → Normal file
View File

0
docs/blocks.md Executable file → Normal file
View File

0
docs/changelog.md Executable file → Normal file
View File

0
docs/cn/binds.md Executable file → Normal file
View File

0
docs/cn/cdf.md Executable file → Normal file
View File

0
docs/cn/index.md Executable file → Normal file
View File

0
docs/cn/intro.md Executable file → Normal file
View File

0
docs/en/box.md Executable file → Normal file
View File

0
docs/imgs/hello_window.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

0
github-speed-up.md Executable file → Normal file
View File

0
kivyblocks/__init__.py Executable file → Normal file
View File

0
kivyblocks/android_rotation.py Executable file → Normal file
View File

0
kivyblocks/anz/__init__.py Executable file → Normal file
View File

0
kivyblocks/anz/bluetooth.py Executable file → Normal file
View File

0
kivyblocks/aplayer.py Executable file → Normal file
View File

0
kivyblocks/backendfunction.py Executable file → Normal file
View File

47
kivyblocks/baseWidget.py Executable file → Normal file
View File

@ -75,7 +75,7 @@ from .threadcall import HttpClient
from .i18n import I18n
from .widget_css import WidgetCSS
from .ready import WidgetReady
from .utils import CSize
from .utils import CSize, SUPER
from .swipebehavior import SwipeBehavior
from .widgetExt.inputext import MyDropDown
@ -89,6 +89,16 @@ font_names = {
'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
from .widgetExt.androidwebview import AWebView
@ -130,7 +140,7 @@ def ScatterBox(WidgetCSS, WidgetReady, ScatterLayout):
class Box(WidgetCSS, WidgetReady, BoxLayout):
def __init__(self, **kw):
try:
super(Box, self).__init__(**kw)
SUPER(Box, self, kw)
except Exception as e:
print('Box(',kw,') Error')
raise e
@ -147,17 +157,17 @@ class VBox(Box):
Box.__init__(self, **kw)
class SwipeBox(SwipeBehavior, Box):
pass
def __init__(self, **kw):
SUPER(SwipeBox, self, kw)
class Text(Label):
lang=StringProperty('')
otext = StringProperty('')
def __init__(self,i18n=False, texttype='text', wrap=False, rate=1,
def __init__(self,i18n=False, texttype='text', wrap=False,
fgcolor=None, **kw):
app = App.get_running_app()
self.rate = 1
fontsize = app.get_font_size(texttype) * self.rate
fontsize = font_sizes.get(texttype)
fontname = font_names.get(texttype)
self._i18n = i18n
self.i18n = I18n()
@ -172,9 +182,7 @@ class Text(Label):
if not kwargs.get('text'):
kwargs['text'] = kwargs.get('otext','')
super(Text, self).__init__(**kwargs)
app.bind(font_size=self.reset_font_size)
# self.bind(texture_size=self.setter('size'))
SUPER(Text, self, kwargs)
if self._i18n:
self.i18n.addI18nWidget(self)
if self.wrap:
@ -184,9 +192,6 @@ class Text(Label):
if self.bgcolor:
self.color = self.bgcolor
def reset_font_size(self, app, fsize):
self.font_size = fsize * self.rate
def resize(self, *args):
if not self.size_hint_y:
ps = [0,0,0,0]
@ -272,7 +277,6 @@ class Modal(VBox):
self._target = None
super(Modal, self).__init__(**kw)
self.set_size_position()
print('size_hint=', self.size_hint, kw)
self._target.bind(size=self.set_size_position)
self.register_event_type('on_open')
self.register_event_type('on_pre_open')
@ -295,7 +299,7 @@ class Modal(VBox):
return super().on_touch_down(touch)
def on_target(self, o, target):
def on_target(self):
w = Window
if self.target is not None:
w = Factory.Blocks.getWidgetById(self.target)
@ -320,9 +324,9 @@ class Modal(VBox):
self.width = self.size_hint_x * self._target.width
if self.size_hint_y:
self.height = self.size_hint_y * self._target.height
print("size with target:", self.width, self.height,
self.size_hint, self.size_hint,
"target size=", self._target.size
print(self.width, self.height,
self.size_hint_x, self.size_hint_y,
self._target.size
)
self.set_modal_position()
@ -339,7 +343,7 @@ class Modal(VBox):
x = 0
if yn == 'c':
y = (self._target.height - self.height) / 2
elif yn == 'b':
elif yn == 't':
y = self._target.height - self.height
if y < 0:
y = 0
@ -347,9 +351,6 @@ class Modal(VBox):
self.pos = x, y
else:
self.pos = self._target.pos[0] + x, self._target.pos[1] + y
Mx = Window.width
My = Window.height
self.pos_hint = {'x':self.pos[0]/Mx, 'y':self.pos[1]/My}
def open(self):
if self.parent:
@ -388,7 +389,7 @@ class TimedModal(Modal):
show_time = NumericProperty(0)
def __init__(self, **kw):
self.time_task = None
super(TimedModal, self).__init__(**kw)
SUPER(TimedModal, self, kw)
def open(self, *args, **kw):
if self.time_task is not None:

0
kivyblocks/bgcolorbehavior.py Executable file → Normal file
View File

0
kivyblocks/block_test.py Executable file → Normal file
View File

25
kivyblocks/blocks.py Executable file → Normal file
View File

@ -204,7 +204,7 @@ x = ClassX{klass_cnt}()
return None
if url.startswith('file://'):
return self.script.dispatch(url, params)
return self.script.dispatch(url, **params)
elif url.startswith('http://') or url.startswith('https://'):
try:
hc = HttpClient()
@ -214,14 +214,7 @@ x = ClassX{klass_cnt}()
print_exc()
if errback:
return errback(None,e)
return {
"widgettype":"Text",
"options":{
"text":f"{url=} error",
"wrap":True,
"halign":"left"
}
}
return None
else:
config = getConfig()
url = config.uihome + url
@ -734,7 +727,7 @@ x = ClassX{klass_cnt}()
print('Block3: desc must be a dict object not None')
return None
w = doit(desc)
# print('widgetBuild():w=', w)
print('widgetBuild():w=', w)
return w
@classmethod
@ -788,16 +781,8 @@ x = ClassX{klass_cnt}()
return _find_widget(name,
from_widget=from_widget.parent,
dir=dir)
wid = None
if hasattr(from_widget, 'widget_id'):
wid = getattr(from_widget, 'widget_id')
if wid == 'channel_list':
print('channel_list=', from_widget.__class__.__name__, 'children cnt=', len(from_widget.children))
for c in children:
cid = None
if hasattr(c, 'widget_id'):
cid = getattr(c, 'widget_id')
print('channel_list children:', c.__class__.__name__, cid)
Logger.info('Block:_find_widget(), %s, %s %s return None',
name, from_widget.__class__.__name__, dir)
return None
def find_widget(step, from_widget):

41
kivyblocks/blocksapp.py Executable file → Normal file
View File

@ -12,7 +12,6 @@ from appPublic.folderUtils import ProgramPath
from appPublic.uniqueID import getID
from appPublic.rsawrap import RSA
from kivy.properties import NumericProperty
from kivy.factory import Factory
from kivy.metrics import sp,dp,mm
from kivy.core.window import WindowBase, Window
@ -49,7 +48,6 @@ signal.signal(signal.SIGINT, signal_handler)
class BlocksApp(App):
font_size = NumericProperty(24)
def get_rotation(self):
return get_rotation()
@ -62,13 +60,10 @@ class BlocksApp(App):
with codecs.open(config.css.css_filename, 'r', 'utf-8') as f:
d = json.load(f)
self.buildCsses(d)
try:
if config.css.css_url:
hc = HttpClient()
d = hc.get(self.realurl(config.css.css_url))
self.buildCsses(d)
except:
pass
def on_rotate(self,*largs):
self.current_rotation = Window.rotation
@ -80,47 +75,21 @@ class BlocksApp(App):
if isinstance(v,dict):
register_css(k,v)
def set_fontsize(self, x):
self.font_size = x
def get_font_size(self, ttype='text'):
text_fontrates = {
'text':1,
'title6':1.1,
'title5':1.3,
'title4':1.5,
'title3':1.7,
'title2':1.9,
'title1':2.1
}
v = text_fontrates.get(ttype, 'text')
return v * self.font_size
def build(self):
tl = Label(text='test')
self.font_size = tl.font_size
print('##################app_font_size=', self.font_size)
config = getConfig()
self.workers = Workers(maxworkers=config.maxworkers or 80)
self.workers.start()
try:
i18n = I18n()
except:
i18n = None
self.platform = platform
self.is_desktop = platform in ['win', 'linux', 'macosx']
self.default_params = {}
if config.default_params:
self.default_params.update(config.default_params)
config = getConfig()
self.public_headers = {
"client_uuid":getID(),
"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)
self.workers = Workers(maxworkers=config.maxworkers or 80)
self.workers.start()
self.load_csses()
self.running = True
if config.root:
@ -194,7 +163,7 @@ class BlocksApp(App):
}
self.public_headers.update(device)
def on_close(self, *args, **kwargs):
def on_close(self, *args):
self.workers.running = False
return False

10
kivyblocks/boxViewer.py Executable file → Normal file
View File

@ -29,7 +29,7 @@ from kivy.graphics import Fbo, Color, Rectangle
from kivy.properties import NumericProperty, StringProperty, DictProperty
from .responsivelayout import VResponsiveLayout
from .toolbar import Toolbar
from .paging import RelatedLoader
from .paging import Paging, RelatedLoader
from .utils import CSize, SUPER
from .ready import WidgetReady
from .baseWidget import VBox
@ -122,9 +122,15 @@ class BoxViewer(VBox):
idx = data['idx']
widgets = data['widgets']
with self.fboContext():
self._fbo = Fbo(size=self.size)
with self._fbo:
self._background_color = Color(0,0,0,1)
self._background_rect = Rectangle(size=self.size)
for r in recs:
self.showObject(widgets, r, index=idx)
with self.canvas:
self._fbo_rect = Rectangle(size=self.size,
texture=self._fbo.texture)
self.subwidgets += widgets

0
kivyblocks/circle_progress.py Executable file → Normal file
View File

41
kivyblocks/clickable.py Executable file → Normal file
View File

@ -3,12 +3,6 @@ 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.uix.anchorlayout import AnchorLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.stacklayout import StackLayout
from kivy.factory import Factory
from kivy.uix.image import AsyncImage
from kivy.properties import NumericProperty, DictProperty, \
@ -401,34 +395,6 @@ def build_cmdbox_view(desc, rec=None):
x.setValue(vd)
return x
class PressableAnchorLayout(TouchRippleButtonBehavior, AnchorLayout):
def on_press(self, o=None, d=None):
pass
class PressableBoxLayout(TouchRippleButtonBehavior, BoxLayout):
def on_press(self, o=None, d=None):
pass
class PressableFloatLayout(TouchRippleButtonBehavior, FloatLayout):
def on_press(self, o=None, d=None):
pass
class PressableRelativeLayout(TouchRippleButtonBehavior, RelativeLayout):
def on_press(self, o=None, d=None):
pass
class PressableGridLayout(TouchRippleButtonBehavior, GridLayout):
def on_press(self, o=None, d=None):
pass
class PressableScatterLayout(TouchRippleButtonBehavior, ScatterLayout):
def on_press(self, o=None, d=None):
pass
class PressableStackLayout(TouchRippleButtonBehavior, StackLayout):
def on_press(self, o=None, d=None):
pass
UiFactory.register('checkbox', build_checkbox, build_checkbox)
UiFactory.register('cmdbox', build_cmdbox_view, build_cmdbox_view)
@ -443,10 +409,3 @@ r('ToggleText',ToggleText)
r('ToggleIconText',ToggleIconText)
r('ClickableImage',ClickableImage)
r('ToggleImage',ToggleImage)
r('PressableAnchorLayout',PressableAnchorLayout)
r('PressableBoxLayout',PressableBoxLayout)
r('PressableFloatLayout',PressableFloatLayout)
r('PressableRelativeLayout',PressableRelativeLayout)
r('PressableGridLayout',PressableGridLayout)
r('PressableScatterLayout',PressableScatterLayout)
r('PressableStackLayout',PressableStackLayout)

0
kivyblocks/color_definitions.py Executable file → Normal file
View File

0
kivyblocks/colorcalc.py Executable file → Normal file
View File

0
kivyblocks/command_action.py Executable file → Normal file
View File

View File

@ -1,146 +0,0 @@
# container is ScrollView with a dataloader
import json
from appPublic.myTE import MyTemplateEngine
from kivy.uix.scrollview import ScrollView
from kivy.properties import DictProperty, ListProperty, BooleanProperty, StringProperty, NumericProperty
from kivy.clock import Clock
from kivyblocks.blocks import Blocks
from kivyblocks.paging import RelatedLoader
from kivyblocks.widget_css import WidgetCSS
class LayoutBuildFailed(Exception):
def __init__(self, desc):
self.desc = desc
def __str__(self):
return str(self.desc)
def __expr__(self):
return str(self)
class Container(WidgetCSS, ScrollView):
"""
"loader":{
"page":1,
"rows":60,
"dataurl":"eeee",
"params":{
k:v
}
},
"""
idField = StringProperty('id')
loader = DictProperty({})
data = ListProperty(None)
layout = DictProperty({})
viewer = DictProperty({})
min_threhold = NumericProperty(0.01)
max_htrehold = NumericProperty(0.99)
extend_x = BooleanProperty(False)
content = None
def __init__(self, **kw):
self.dataloader = None
self.engine = MyTemplateEngine('.')
self.id_widget = {}
super(Container, self).__init__(**kw)
self.content = Blocks().widgetBuild(self.layout)
if not self.content:
raise LayoutBuildFailed
if self.do_scroll_x:
self.content.bind(minimum_width=self.content.setter('width'))
if self.do_scroll_y:
self.content.bind(minimum_height=self.content.setter('height'))
self.add_widget(self.content)
self.viewer_tmpl = json.dumps(self.viewer)
self.bind(on_scroll_stop = self.on_scrollstop)
if self.data:
d = {
"dir":"down",
"rows":self.data,
"total":len(self.data)
}
self.add_page(d)
if self.loader:
self.dataloader = RelatedLoader(target=self, **self.loader)
self.dataloader.bind(on_deletepage=self.delete_page)
self.dataloader.bind(on_pageloaded=self.add_page)
self.dataloader.bind(on_newbegin=self.clear_records)
Clock.schedule_once(self.loadData, 0.5)
def print_info(self, *args):
print(f"{self.scroll_distance=}, {self.scroll_timeout=}, {self.do_scroll_x=} {self.do_scroll_y=}, {self.scroll_x=}, {self.scroll_y=}, {self.content.size=}")
def parse_viewer(self, rec):
return json.loads(self.engine.renders(self.viewer_tmpl, rec))
def loadData(self, *args, **kw):
kw['page'] = 1
self.dataloader.do_search(None,kw)
def add_record(self, rec, tail=True):
desc = self.parse_viewer(rec)
w = Blocks().widgetBuild(desc)
if w:
idx = 0
if not tail:
idx = -1
id = rec.get(self.idField)
self.id_widget[id] = w
self.content.add_widget(w, index=idx)
else:
print(f'create widget error {self.viewer_tmpl=}, {rec=}')
def delete_record(self, rec):
id = rec.get(self.idField)
w = self.id_widget.get(id)
if w:
self.content.remove_widget(w)
del self.id_widget[id]
def delete_page(self, o, d):
# print(f'delete_page():{o=}, {d=}')
for r in d:
self.delete_record(r)
def add_page(self, o, d):
# print(f'add_page():{o=},{d=}')
dir = d.get('dir', 'down')
recs = d.get('data', [])
tail = True
if dir != 'down':
tail = False
recs.reverse()
for r in recs:
self.add_record(r, tail=tail)
if self.extend_x:
self.scroll_x = d['locator']
else:
self.scroll_y = d['locator']
def clear_records(self, o):
# print(f'clear_records():{o=}')
self.content.clear_widgets()
self.id_Widget = {}
def on_scrollstop(self, o, d=None):
if self.dataloader is None:
return
if self.extend_x:
if self.scroll_x < self.min_threhold:
self.dataloader.loadNextPage()
if self.scroll_x > self.max_htrehold:
self.dataloader.loadPreviousPage()
print('return here ...')
return
if self.dataloader.loading:
print('loading is True')
return
if self.scroll_y < self.min_threhold:
print('load next page ...')
self.dataloader.loadNextPage()
if self.scroll_y > self.max_htrehold:
print('load previous page ...')
self.dataloader.loadPreviousPage()

0
kivyblocks/dataloader.py Executable file → Normal file
View File

0
kivyblocks/date_picker.py Executable file → Normal file
View File

0
kivyblocks/dateinput.py Executable file → Normal file
View File

1
kivyblocks/defaultimage.py Executable file → Normal file
View File

@ -1,4 +1,3 @@
import os
from kivy.uix.image import AsyncImage
from kivy.factory import Factory
from kivy.properties import StringProperty

84
kivyblocks/dg.py Executable file → Normal file
View File

@ -10,6 +10,7 @@ from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.graphics import Fbo
from kivy.uix.button import ButtonBehavior
from kivy.clock import Clock
from kivy.properties import BooleanProperty, StringProperty, \
@ -24,9 +25,9 @@ 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 RelatedLoader
from .paging import Paging, RelatedLoader
from .ready import WidgetReady
from .toolbar import Toolbar
from .bgcolorbehavior import BGColorBehavior
@ -55,19 +56,17 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout):
"""
self.desc = desc
self.row = row
width = self.desc['width']
height = self.row.part.datagrid.rowHeight()
# print(f'Cell:{width=}, {height=},value={desc["value"]}')
self.part = self.row.part
self.datagrid = self.part.datagrid
super().__init__(size_hint=(None,None),
width=width,
bgcolor=[0.1,0.5,0.5],
height=height
width = self.desc['width'],
height = self.row.part.datagrid.rowHeight()
)
self.csscls=self.row.part.datagrid.row_normal_css
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'
)
@ -75,6 +74,14 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout):
else:
self.cell_type = 'data'
bl = UiFactory.build_view_widget(desc,self.row.row_data)
if self.row.header and desc['name'] == '_checkable_action_':
id = self.row.row_data[idfield]
if self.row.part.datagrid.is_checked(id):
bl.setValue(True)
else:
bl.setValue(False)
bl.bind(select_state=self.row.checkable_action)
self.colume_name = desc['name']
if bl:
self.add_widget(bl)
@ -92,6 +99,7 @@ class Row(BoxLayout):
"""
self.part = part
self.datagrid = self.part.datagrid
self.header = header
self.row_data = data
self.row_id = None
@ -106,6 +114,9 @@ class Row(BoxLayout):
self.height = self.part.datagrid.rowHeight()
self.init(0)
def checkable_action(self, o, *args):
self.datagrid.set_checked_rows(self.row_id, o.select_state)
def on_row_press(self, *args):
pass
@ -151,7 +162,6 @@ class Header(WidgetReady, ScrollPanel):
for i,f in enumerate(rd) ]
self.header = Row(self.part,rd,header=True)
self.add_widget(self.header)
self.size_hint_y = None
self.height = self.header.height
class Body(WidgetReady, ScrollPanel):
@ -317,6 +327,7 @@ class DataGrid(VBox):
]
}
"""
checkable = BooleanProperty(False)
row_selected = BooleanProperty(False)
row_normal_css = StringProperty('default')
row_selected_css = StringProperty('default')
@ -330,6 +341,16 @@ class DataGrid(VBox):
fields = ListProperty(None)
tailer = DictProperty(None)
def __init__(self,**options):
if self.checkable:
self.fields.insert(0, {
"name":"_checkable_action_",
"label":" ",
"type":"checkbox",
"width":CSize(2),
"freeze_flag":True
})
self.checked_rows = []
self.select_rowid = None
self.rowheight = None
self.on_sizeTask = None
@ -367,11 +388,16 @@ class DataGrid(VBox):
self.tailer_widgets = {}
self.build_tailer(self.tailer)
def set_checked_rows(self, rowid, flag):
if flag and rowid not in self.checked_ids:
self.checked_ids.append(rowid)
if not flag:
self.checked_ids = [ id for id in self.checked_ids if id !=rowid]
def on_rowpress(self, *args):
print('on_rowpress fire, args=', args)
def on_cellpress(self, *args):
print('on_cellpress fire, args=', args)
print('on_cesspress fire, args=', args)
def on_headerpress(self, *args):
print('on_headerpress fire, args=', args)
@ -436,7 +462,7 @@ class DataGrid(VBox):
desc = {
"widgettype":"Text",
"options":{
"text":n,
"otext":n,
"i18n":True,
}
}
@ -529,9 +555,17 @@ class DataGrid(VBox):
idx = -1
recs1 = recs[:self.show_rows]
recs2 = recs[self.show_rows:]
self._fbo = Fbo(size=self.size)
with self._fbo:
self._background_color = Color(0,0,0,1)
self._background_rect = Rectangle(size=self.size)
for r in recs1:
id = self.addRow(r,index=idx)
ids.append(id)
with self.canvas:
self._fbo_rect = Rectangle(size=self.size,
texture=self._fbo.texture)
data['idx'] = idx
data['ids'] = ids
data['data'] = recs2
@ -543,9 +577,16 @@ class DataGrid(VBox):
page = data['page']
idx = data['idx']
ids = data['ids']
self._fbo = Fbo(size=self.size)
with self._fbo:
self._background_color = Color(0,0,0,1)
self._background_rect = Rectangle(size=self.size)
for r in recs:
id = self.addRow(r,index=idx)
ids.append(id)
with self.canvas:
self._fbo_rect = Rectangle(size=self.size,
texture=self._fbo.texture)
self._dataloader.bufferObjects(page,ids)
x = self._dataloader.getLocater()
self.locater(x)
@ -555,13 +596,11 @@ class DataGrid(VBox):
self.delRow(id)
def addRow(self,data, **kw):
# print('#########addRow() ---------')
id = getID()
f_row = None
if self.freeze_part:
self.freeze_part.body.addRow(id, data, **kw)
self.normal_part.body.addRow(id, data, **kw)
# print('#########addRow()==========')
return id
def delRow(self,id,**kw):
@ -600,23 +639,6 @@ class DataGrid(VBox):
fs.append(f)
return fs
def show_size(self, *args, **kw):
print(f'dg:{self.size}')
print(f'normal_part:{self.normal_part.size}')
print(f'body:{self.normal_part.body.size}')
print(f'row:{self.normal_part.body.children[0].size}')
print(f'row:{self.normal_part.body.children[0].size}')
sp = self.normal_part.body.children[0]
print(f'rows={len(sp.children) - 1}')
for i, r in enumerate(sp.children):
print(f'{i} {r.size=}')
if self.normal_part.parent.parent == self:
print('part ok')
else:
print('part parent=', self.normal_part.parent)
if self.normal_part.body.parent == self.normal_part:
print('body ok')
def get_selected_data(self):
if not self.selected_rowid:
return {}

0
kivyblocks/download.py Executable file → Normal file
View File

View File

@ -1,12 +0,0 @@
import inspect
import asyncio
from functools import wraps
def eventhandler(func):
@wraps(func)
def wrapper_func(*args, **kw):
if inspect.iscoroutinefunction(func):
return asyncio.gather(func(*args, **kw))
return func(*args, **kw)
return wrapper_func

0
kivyblocks/externalwidgetmanager.py Executable file → Normal file
View File

395
kivyblocks/ffpyplayer_video.py Executable file → Normal file
View File

@ -35,6 +35,7 @@ vhks = ['v_src',
]
class FFVideo(WidgetReady, VideoBehavior, BoxLayout):
# class FFVideo(VideoBehavior, BoxLayout):
def __init__(self, **kw):
kw1 = {k:v for k,v in kw.items() if k in vhks}
kw1['renderto'] = 'background'
@ -43,8 +44,402 @@ class FFVideo(WidgetReady, VideoBehavior, BoxLayout):
BoxLayout.__init__(self, **kw2)
VideoBehavior.__init__(self, **kw1)
WidgetReady.__init__(self)
# self.msg_w = Label(text='Hello')
# self.add_widget(self.msg_w)
# self.bind(on_frame=self.show_info)
def show_info(self, o, d):
txt = f"size={d['size']}, vpos={d['pos']}, vsize={d['texture_size']}"
self.msg_w.text = txt
"""
class FFVideo(WidgetReady, Image):
v_src = StringProperty(None)
status = OptionProperty('stop', \
options=['stop', 'play', 'pause'])
play_mode = OptionProperty('normal', \
options=['normal', 'preview', 'audioonly'])
header_callback = StringProperty(None)
audio_id = NumericProperty(None)
duration = NumericProperty(-1)
position = NumericProperty(-1)
volume = NumericProperty(-1)
timeout = NumericProperty(5)
in_center_focus = BooleanProperty(False)
def __init__(self, **kwargs):
self._player = None
self._update_task = None
self.running = None
self.vh_task = None
self.is_black = False
self.videosize = None
self.ff_opts = {
"framedrop":True
}
self.lib_opts = {}
self.headers_pattern = {}
super(FFVideo, self).__init__(**kwargs)
self.set_black()
self.start_task = None
self.block_task = None
self.register_event_type('on_frame')
self.register_event_type('on_open_failed')
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 video_blocked(self, *args):
self._play_stop()
self.on_v_src(None, None)
def on_open_failed(self, source):
print(f'{source} open failed')
def on_load_success(self, *args):
pass
def on_enter_focus(self, *args):
pass
def on_leave_focus(self, *args):
pass
def check_focus(self,*args):
if not self.parent:
self.in_center_screen = False
return
if not self.get_root_window():
self.in_center_screen = False
w = self.parent
pos = w.to_widget(*Window.center)
if self.collide_point(*pos):
self.in_center_screen = True
else:
self.in_center_screen = False
def set_ff_opt(self, k,v):
self.ff_opts.update({k:v})
def set_lib_opt(self, k, v):
self.lib_opts.update({k:v})
def set_pattern_header(self, pattern, k,v):
dic = self.headers_pattern.get(pattern,{})
dic.update({k:v})
self.headers_pattern[pattern] = dic
def _get_spec_headers(self, filename):
for p in self.headers_pattern.keys():
if filename.startswith(p):
headers = self.headers_pattern[p]
headers_str = ''.join([f'{k}:{v}\r\n' for k,v in headers.items()])
return headers_str
return None
def on_volume(self, *args):
if self._player is None:
return
self._player.set_volume(self.volume)
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':
self._play_stop()
else:
pass
def on_parent(self, *args):
if self.parent:
return
# self._play_stop()
print('on_parent() called -----------')
def on_startplay(self, *args):
pass
def on_frame(self, *args):
if self._player is None:
return
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):
self._play_stop()
def set_volume(self, v):
if self.play_mode == 'preview':
return
if self._player is None:
return
if self.status != 'play':
return
if v > 1.0:
v = 1.0
if v < 0.0:
v = 0.0
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, relative=False)
self.position = self._player.get_pts()
self.last_val = None
def mute(self, flag=None):
if self.play_mode == 'preview':
return
if self._player is None:
return
if self.status != 'play':
return
x = self._player.get_mute()
print('Video(), mute=', x)
self._player.set_mute(not x)
def switch_audio(self):
if self.play_mode == 'preview':
return
if self._player is None:
return
if self.status != 'play':
return
self._player.request_channel('audio', action='cycle')
def on_v_src(self, o, src):
# self.running = Running(self)
self.status = 'stop'
self.playing = False
ff_opts = {
'pause':False
}
if self.play_mode == 'preview':
ff_opts['lowres'] = 2 # 1/4 size
ff_opts['an'] = True
elif self.play_mode == 'audioonly':
ff_opts['vn'] = True
ff_opts.update(self.ff_opts)
lib_opts = {
}
lib_opts.update(self.lib_opts)
heads = self._get_spec_headers(self.v_src)
if heads:
lib_opts.update({'headers':heads})
ff_opts.update({'headers':heads})
print('ff_opts=', ff_opts)
print('lib_opts=', lib_opts)
print('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEe')
print_exc()
print('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEe')
self._player = MediaPlayer(self.v_src, ff_opts=ff_opts, \
lib_opts=lib_opts)
# self._play_start()
self.status = 'play'
self.start_task = Clock.schedule_once(self.open_failed, self.timeout)
def open_failed(self, *args):
self.dispatch('on_open_failed', self.v_src)
def on_open_failed(self, source):
print(f'{source} open failed')
def file_opened(self, files):
self.v_src = files[0]
def play(self):
if self._player is None:
return
self.status = 'play'
def pause(self):
if self._player is None:
return
# self._player.set_pause(True)
self.status = 'pause'
def _play_start(self):
self.last_frame = None
self.is_black = False
self.vsync = False
def _get_video_info(self):
if not self.playing:
self.playing = True
meta = self._player.get_metadata()
self.duration = meta['duration']
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:
self._player.close_player()
self._player = None
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 = 0
self.videosize = None
def on_size(self, *args):
if self._player is None:
return
if self.videosize == None:
return
w, h = self.videosize
r = w / h
r1 = self.width / self.height
if r1 >= r:
self._player.set_size(-1, self.height)
else:
self._player.set_size(self.width, -1)
def set_black(self):
if self.is_black:
return
image_texture = Texture.create(
size=self.size, colorfmt='rgb')
buf = b'\x00' * int(self.width * self.height * 3)
image_texture.blit_buffer(buf, colorfmt='rgb', bufferfmt='ubyte')
self.texture = image_texture
self.is_black = True
def show_yuv420(self, img):
w, h = img.get_size()
w2 = int(w / 2)
h2 = int(h / 2)
self._tex_y = Texture.create(
size=(w, h), colorfmt='luminance')
self._tex_u = Texture.create(
size=(w2, h2), colorfmt='luminance')
self._tex_v = Texture.create(
size=(w2, h2), colorfmt='luminance')
self._fbo = fbo = Fbo(size=(w, h))
with fbo:
BindTexture(texture=self._tex_u, index=1)
BindTexture(texture=self._tex_v, index=2)
Rectangle(size=fbo.size, texture=self._tex_y)
fbo.shader.fs = VideoFFPy.YUV_RGB_FS
fbo['tex_y'] = 0
fbo['tex_u'] = 1
fbo['tex_v'] = 2
dy, du, dv, _ = img.to_memoryview()
if dy and du and dv:
self._tex_y.blit_buffer(dy, colorfmt='luminance')
self._tex_u.blit_buffer(du, colorfmt='luminance')
self._tex_v.blit_buffer(dv, colorfmt='luminance')
self._fbo.ask_update()
self._fbo.draw()
texture.flip_vertical()
self.texture = texture
def show_others(self, img):
w, h = img.get_size()
texture = Texture.create(size=(w, h), colorfmt='rgb')
texture.blit_buffer(
img.to_memoryview()[0], colorfmt='rgb',
bufferfmt='ubyte')
texture.flip_vertical()
self.texture = texture
# print('img_size=', w, h, 'window size=', self.size)
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':
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:
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
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):
if self.start_task:
self.start_task.cancel()
self.start_task = None
if self.block_task:
self.block_task.cancel()
# if self.running:
# print('runnung dismiss ........')
# self.running.dismiss()
# self.running = None
self.position = self._player.get_pts()
self.volume = self._player.get_volume()
img, t = self.last_frame
if self._out_fmt == 'yuv420p':
self.show_yuv420(img)
else:
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)
self.block_task = Clock.schedule_once(self.video_blocked, self.timeout)
"""

0
kivyblocks/filebrowser.py Executable file → Normal file
View File

1
kivyblocks/form.py Executable file → Normal file
View File

@ -32,7 +32,6 @@ form options
"method"
}
"cols":"1"
"inputwidth",
"labelwidth":
"textsize":
"inputheight":

0
kivyblocks/gps.py Executable file → Normal file
View File

0
kivyblocks/hack_ffpyplayer.py Executable file → Normal file
View File

0
kivyblocks/hierarchy.py Executable file → Normal file
View File

0
kivyblocks/hostimage.py Executable file → Normal file
View File

0
kivyblocks/httpvplayer.py Executable file → Normal file
View File

6
kivyblocks/i18n.py Executable file → Normal file
View File

@ -58,12 +58,9 @@ class I18n:
if config.i18n_url:
url = '%s%s' % (config.uihome, config.i18n_url)
hc = HttpClient()
try:
d = hc.get(url)
if isinstance(d, list):
return d
except:
pass
return []
def loadI18n(self,lang):
@ -77,12 +74,9 @@ class I18n:
if config.i18n_url:
url = '%s%s/%s' % (config.uihome, config.i18n_url, lang)
hc = HttpClient()
try:
d = hc.get(url)
print('i18n() %s get data=' % url, d, type(d))
self.kvlang[lang] = d
except:
pass
def __call__(self,msg,lang=None):
if lang is None:

Binary file not shown.

0
kivyblocks/imgs/Mute64x64.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

0
kivyblocks/imgs/Play64X64.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

0
kivyblocks/imgs/Sound64x64.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

0
kivyblocks/imgs/backword.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

0
kivyblocks/imgs/broken.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

0
kivyblocks/imgs/bullet_arrow_down.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

0
kivyblocks/imgs/bullet_arrow_right.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 315 B

0
kivyblocks/imgs/cancel.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

0
kivyblocks/imgs/checkbox-off.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

0
kivyblocks/imgs/checkbox-on.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
kivyblocks/imgs/clear.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

0
kivyblocks/imgs/conform.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

0
kivyblocks/imgs/crud_add.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
kivyblocks/imgs/crud_browser.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

0
kivyblocks/imgs/crud_del.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
kivyblocks/imgs/crud_edit.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

0
kivyblocks/imgs/crud_filter.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

0
kivyblocks/imgs/doing.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

0
kivyblocks/imgs/download.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

0
kivyblocks/imgs/error.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

0
kivyblocks/imgs/folder.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

0
kivyblocks/imgs/icon_view.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

0
kivyblocks/imgs/info.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

0
kivyblocks/imgs/kivymd_512.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

0
kivyblocks/imgs/kivymd_logo.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

0
kivyblocks/imgs/list_view.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

0
kivyblocks/imgs/loading.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

0
kivyblocks/imgs/loading1.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

0
kivyblocks/imgs/menu.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

0
kivyblocks/imgs/musictrack.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

0
kivyblocks/imgs/musictrack_d.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

0
kivyblocks/imgs/next.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

0
kivyblocks/imgs/next_d.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

0
kivyblocks/imgs/org_sound.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

0
kivyblocks/imgs/org_sound_d.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

0
kivyblocks/imgs/origin.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

0
kivyblocks/imgs/pause.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

0
kivyblocks/imgs/pause_d.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

0
kivyblocks/imgs/picture_empty.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 463 B

0
kivyblocks/imgs/play.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

0
kivyblocks/imgs/play_d.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

0
kivyblocks/imgs/power64x64.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

0
kivyblocks/imgs/previous.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
kivyblocks/imgs/quad_shadow-0.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

0
kivyblocks/imgs/quad_shadow-1.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

0
kivyblocks/imgs/quad_shadow-2.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
kivyblocks/imgs/quad_shadow.atlas Executable file → Normal file
View File

0
kivyblocks/imgs/question.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

0
kivyblocks/imgs/rec_shadow-0.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Some files were not shown because too many files have changed in this diff Show More