Compare commits
10 Commits
1c17b6165c
...
9e2b5db1a6
Author | SHA1 | Date | |
---|---|---|---|
|
9e2b5db1a6 | ||
|
01442309d9 | ||
|
f1e493ca8e | ||
|
313274b376 | ||
|
9e60443e79 | ||
|
263636779b | ||
|
8e9719ac57 | ||
|
2532efa9f2 | ||
|
ef5c160c40 | ||
|
c3ef3a99b3 |
0
docs/blocks.md
Normal file → Executable file
0
docs/changelog.md
Normal file → Executable file
0
docs/cn/binds.md
Normal file → Executable file
0
docs/cn/cdf.md
Normal file → Executable file
0
docs/cn/index.md
Normal file → Executable file
0
docs/cn/intro.md
Normal file → Executable file
0
docs/en/box.md
Normal file → Executable file
0
docs/imgs/hello_window.png
Normal file → Executable file
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
0
github-speed-up.md
Normal file → Executable file
0
kivyblocks/__init__.py
Normal file → Executable file
0
kivyblocks/android_rotation.py
Normal file → Executable file
0
kivyblocks/anz/__init__.py
Normal file → Executable file
0
kivyblocks/anz/bluetooth.py
Normal file → Executable file
0
kivyblocks/aplayer.py
Normal file → Executable file
0
kivyblocks/backendfunction.py
Normal file → Executable file
47
kivyblocks/baseWidget.py
Normal file → Executable 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, SUPER
|
||||
from .utils import CSize
|
||||
from .swipebehavior import SwipeBehavior
|
||||
from .widgetExt.inputext import MyDropDown
|
||||
|
||||
@ -89,16 +89,6 @@ 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
|
||||
@ -140,7 +130,7 @@ def ScatterBox(WidgetCSS, WidgetReady, ScatterLayout):
|
||||
class Box(WidgetCSS, WidgetReady, BoxLayout):
|
||||
def __init__(self, **kw):
|
||||
try:
|
||||
SUPER(Box, self, kw)
|
||||
super(Box, self).__init__(**kw)
|
||||
except Exception as e:
|
||||
print('Box(',kw,') Error')
|
||||
raise e
|
||||
@ -157,17 +147,17 @@ class VBox(Box):
|
||||
Box.__init__(self, **kw)
|
||||
|
||||
class SwipeBox(SwipeBehavior, Box):
|
||||
def __init__(self, **kw):
|
||||
SUPER(SwipeBox, self, kw)
|
||||
|
||||
pass
|
||||
|
||||
class Text(Label):
|
||||
lang=StringProperty('')
|
||||
otext = StringProperty('')
|
||||
def __init__(self,i18n=False, texttype='text', wrap=False,
|
||||
def __init__(self,i18n=False, texttype='text', wrap=False, rate=1,
|
||||
fgcolor=None, **kw):
|
||||
|
||||
fontsize = font_sizes.get(texttype)
|
||||
app = App.get_running_app()
|
||||
self.rate = 1
|
||||
fontsize = app.get_font_size(texttype) * self.rate
|
||||
fontname = font_names.get(texttype)
|
||||
self._i18n = i18n
|
||||
self.i18n = I18n()
|
||||
@ -182,7 +172,9 @@ class Text(Label):
|
||||
if not kwargs.get('text'):
|
||||
kwargs['text'] = kwargs.get('otext','')
|
||||
|
||||
SUPER(Text, self, kwargs)
|
||||
super(Text, self).__init__(**kwargs)
|
||||
app.bind(font_size=self.reset_font_size)
|
||||
# self.bind(texture_size=self.setter('size'))
|
||||
if self._i18n:
|
||||
self.i18n.addI18nWidget(self)
|
||||
if self.wrap:
|
||||
@ -192,6 +184,9 @@ 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]
|
||||
@ -277,6 +272,7 @@ 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')
|
||||
@ -299,7 +295,7 @@ class Modal(VBox):
|
||||
|
||||
return super().on_touch_down(touch)
|
||||
|
||||
def on_target(self):
|
||||
def on_target(self, o, target):
|
||||
w = Window
|
||||
if self.target is not None:
|
||||
w = Factory.Blocks.getWidgetById(self.target)
|
||||
@ -324,9 +320,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(self.width, self.height,
|
||||
self.size_hint_x, self.size_hint_y,
|
||||
self._target.size
|
||||
print("size with target:", self.width, self.height,
|
||||
self.size_hint, self.size_hint,
|
||||
"target size=", self._target.size
|
||||
)
|
||||
self.set_modal_position()
|
||||
|
||||
@ -343,7 +339,7 @@ class Modal(VBox):
|
||||
x = 0
|
||||
if yn == 'c':
|
||||
y = (self._target.height - self.height) / 2
|
||||
elif yn == 't':
|
||||
elif yn == 'b':
|
||||
y = self._target.height - self.height
|
||||
if y < 0:
|
||||
y = 0
|
||||
@ -351,6 +347,9 @@ 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:
|
||||
@ -389,7 +388,7 @@ class TimedModal(Modal):
|
||||
show_time = NumericProperty(0)
|
||||
def __init__(self, **kw):
|
||||
self.time_task = None
|
||||
SUPER(TimedModal, self, kw)
|
||||
super(TimedModal, self).__init__(**kw)
|
||||
|
||||
def open(self, *args, **kw):
|
||||
if self.time_task is not None:
|
||||
|
0
kivyblocks/bgcolorbehavior.py
Normal file → Executable file
0
kivyblocks/block_test.py
Normal file → Executable file
25
kivyblocks/blocks.py
Normal file → Executable 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,7 +214,14 @@ x = ClassX{klass_cnt}()
|
||||
print_exc()
|
||||
if errback:
|
||||
return errback(None,e)
|
||||
return None
|
||||
return {
|
||||
"widgettype":"Text",
|
||||
"options":{
|
||||
"text":f"{url=} error",
|
||||
"wrap":True,
|
||||
"halign":"left"
|
||||
}
|
||||
}
|
||||
else:
|
||||
config = getConfig()
|
||||
url = config.uihome + url
|
||||
@ -727,7 +734,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
|
||||
@ -781,8 +788,16 @@ x = ClassX{klass_cnt}()
|
||||
return _find_widget(name,
|
||||
from_widget=from_widget.parent,
|
||||
dir=dir)
|
||||
Logger.info('Block:_find_widget(), %s, %s %s return None',
|
||||
name, from_widget.__class__.__name__, 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)
|
||||
return None
|
||||
|
||||
def find_widget(step, from_widget):
|
||||
|
51
kivyblocks/blocksapp.py
Normal file → Executable file
@ -12,6 +12,7 @@ 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
|
||||
@ -48,6 +49,7 @@ signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
|
||||
class BlocksApp(App):
|
||||
font_size = NumericProperty(24)
|
||||
def get_rotation(self):
|
||||
return get_rotation()
|
||||
|
||||
@ -60,10 +62,13 @@ class BlocksApp(App):
|
||||
with codecs.open(config.css.css_filename, 'r', 'utf-8') as f:
|
||||
d = json.load(f)
|
||||
self.buildCsses(d)
|
||||
if config.css.css_url:
|
||||
hc = HttpClient()
|
||||
d = hc.get(self.realurl(config.css.css_url))
|
||||
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
|
||||
@ -75,21 +80,47 @@ 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):
|
||||
i18n = I18n()
|
||||
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']
|
||||
config = getConfig()
|
||||
self.default_params = {}
|
||||
if config.default_params:
|
||||
self.default_params.update(config.default_params)
|
||||
|
||||
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:
|
||||
@ -163,7 +194,7 @@ class BlocksApp(App):
|
||||
}
|
||||
self.public_headers.update(device)
|
||||
|
||||
def on_close(self, *args):
|
||||
def on_close(self, *args, **kwargs):
|
||||
self.workers.running = False
|
||||
return False
|
||||
|
||||
|
14
kivyblocks/boxViewer.py
Normal file → Executable 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 Paging, RelatedLoader
|
||||
from .paging import RelatedLoader
|
||||
from .utils import CSize, SUPER
|
||||
from .ready import WidgetReady
|
||||
from .baseWidget import VBox
|
||||
@ -122,15 +122,9 @@ class BoxViewer(VBox):
|
||||
idx = data['idx']
|
||||
widgets = data['widgets']
|
||||
|
||||
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)
|
||||
with self.fboContext():
|
||||
for r in recs:
|
||||
self.showObject(widgets, r, index=idx)
|
||||
|
||||
self.subwidgets += widgets
|
||||
|
||||
|
0
kivyblocks/circle_progress.py
Normal file → Executable file
41
kivyblocks/clickable.py
Normal file → Executable file
@ -3,6 +3,12 @@ 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, \
|
||||
@ -395,6 +401,34 @@ 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)
|
||||
|
||||
@ -409,3 +443,10 @@ 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
Normal file → Executable file
0
kivyblocks/colorcalc.py
Normal file → Executable file
0
kivyblocks/command_action.py
Normal file → Executable file
146
kivyblocks/container.py
Normal file
@ -0,0 +1,146 @@
|
||||
# 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
Normal file → Executable file
0
kivyblocks/date_picker.py
Normal file → Executable file
0
kivyblocks/dateinput.py
Normal file → Executable file
1
kivyblocks/defaultimage.py
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
import os
|
||||
from kivy.uix.image import AsyncImage
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import StringProperty
|
||||
|
1282
kivyblocks/dg.py
Normal file → Executable file
0
kivyblocks/download.py
Normal file → Executable file
12
kivyblocks/eventhandler.py
Normal file
@ -0,0 +1,12 @@
|
||||
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
Normal file → Executable file
395
kivyblocks/ffpyplayer_video.py
Normal file → Executable file
@ -35,7 +35,6 @@ 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'
|
||||
@ -44,402 +43,8 @@ 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
Normal file → Executable file
1
kivyblocks/form.py
Normal file → Executable file
@ -32,6 +32,7 @@ form options
|
||||
"method"
|
||||
}
|
||||
"cols":"1"
|
||||
"inputwidth",
|
||||
"labelwidth":
|
||||
"textsize":
|
||||
"inputheight":
|
||||
|
0
kivyblocks/gps.py
Normal file → Executable file
0
kivyblocks/hack_ffpyplayer.py
Normal file → Executable file
0
kivyblocks/hierarchy.py
Normal file → Executable file
0
kivyblocks/hostimage.py
Normal file → Executable file
0
kivyblocks/httpvplayer.py
Normal file → Executable file
18
kivyblocks/i18n.py
Normal file → Executable file
@ -58,9 +58,12 @@ class I18n:
|
||||
if config.i18n_url:
|
||||
url = '%s%s' % (config.uihome, config.i18n_url)
|
||||
hc = HttpClient()
|
||||
d = hc.get(url)
|
||||
if isinstance(d, list):
|
||||
return d
|
||||
try:
|
||||
d = hc.get(url)
|
||||
if isinstance(d, list):
|
||||
return d
|
||||
except:
|
||||
pass
|
||||
return []
|
||||
|
||||
def loadI18n(self,lang):
|
||||
@ -74,9 +77,12 @@ class I18n:
|
||||
if config.i18n_url:
|
||||
url = '%s%s/%s' % (config.uihome, config.i18n_url, lang)
|
||||
hc = HttpClient()
|
||||
d = hc.get(url)
|
||||
print('i18n() %s get data=' % url, d, type(d))
|
||||
self.kvlang[lang] = d
|
||||
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:
|
||||
|
BIN
kivyblocks/imgs/.DS_Store
vendored
Executable file
0
kivyblocks/imgs/Mute64x64.png
Normal file → Executable file
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
0
kivyblocks/imgs/Play64X64.png
Normal file → Executable file
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
0
kivyblocks/imgs/Sound64x64.png
Normal file → Executable file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
kivyblocks/imgs/add.png
Normal file
After Width: | Height: | Size: 10 KiB |
0
kivyblocks/imgs/backword.png
Normal file → Executable file
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
0
kivyblocks/imgs/broken.png
Normal file → Executable file
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
0
kivyblocks/imgs/bullet_arrow_down.png
Normal file → Executable file
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
0
kivyblocks/imgs/bullet_arrow_right.png
Normal file → Executable file
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
0
kivyblocks/imgs/cancel.png
Normal file → Executable file
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
0
kivyblocks/imgs/checkbox-off.png
Normal file → Executable file
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
0
kivyblocks/imgs/checkbox-on.png
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
kivyblocks/imgs/clear.png
Normal file → Executable file
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
0
kivyblocks/imgs/conform.png
Normal file → Executable file
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
0
kivyblocks/imgs/crud_add.png
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
kivyblocks/imgs/crud_browser.png
Normal file → Executable file
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
0
kivyblocks/imgs/crud_del.png
Normal file → Executable file
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
0
kivyblocks/imgs/crud_edit.png
Normal file → Executable file
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
0
kivyblocks/imgs/crud_filter.png
Normal file → Executable file
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 10 KiB |
BIN
kivyblocks/imgs/deletex.png
Executable file
After Width: | Height: | Size: 14 KiB |
0
kivyblocks/imgs/doing.gif
Normal file → Executable file
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
0
kivyblocks/imgs/download.png
Normal file → Executable file
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
0
kivyblocks/imgs/error.png
Normal file → Executable file
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
0
kivyblocks/imgs/folder.png
Normal file → Executable file
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
0
kivyblocks/imgs/icon_view.png
Normal file → Executable file
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
kivyblocks/imgs/info.png
Normal file → Executable file
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
0
kivyblocks/imgs/kivymd_512.png
Normal file → Executable file
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
0
kivyblocks/imgs/kivymd_logo.png
Normal file → Executable file
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
BIN
kivyblocks/imgs/lensid.png
Executable file
After Width: | Height: | Size: 17 KiB |
0
kivyblocks/imgs/list_view.png
Normal file → Executable file
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
0
kivyblocks/imgs/loading.gif
Normal file → Executable file
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
0
kivyblocks/imgs/loading1.gif
Normal file → Executable file
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
0
kivyblocks/imgs/menu.png
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
kivyblocks/imgs/musictrack.png
Normal file → Executable file
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
0
kivyblocks/imgs/musictrack_d.png
Normal file → Executable file
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
0
kivyblocks/imgs/next.png
Normal file → Executable file
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
0
kivyblocks/imgs/next_d.png
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
kivyblocks/imgs/org_sound.png
Normal file → Executable file
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
0
kivyblocks/imgs/org_sound_d.png
Normal file → Executable file
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
0
kivyblocks/imgs/origin.jpg
Normal file → Executable file
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
0
kivyblocks/imgs/pause.png
Normal file → Executable file
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
0
kivyblocks/imgs/pause_d.png
Normal file → Executable file
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
BIN
kivyblocks/imgs/photo.png
Executable file
After Width: | Height: | Size: 14 KiB |
0
kivyblocks/imgs/picture_empty.png
Normal file → Executable file
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
0
kivyblocks/imgs/play.png
Normal file → Executable file
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
0
kivyblocks/imgs/play_d.png
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
kivyblocks/imgs/power64x64.png
Normal file → Executable file
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
0
kivyblocks/imgs/previous.jpg
Normal file → Executable file
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
0
kivyblocks/imgs/quad_shadow-0.png
Normal file → Executable file
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
0
kivyblocks/imgs/quad_shadow-1.png
Normal file → Executable file
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
0
kivyblocks/imgs/quad_shadow-2.png
Normal file → Executable file
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
0
kivyblocks/imgs/quad_shadow.atlas
Normal file → Executable file
0
kivyblocks/imgs/question.png
Normal file → Executable file
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
0
kivyblocks/imgs/rec_shadow-0.png
Normal file → Executable file
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |