diff --git a/kivyblocks/baseWidget.py b/kivyblocks/baseWidget.py index 29b0a11..7b900dd 100755 --- a/kivyblocks/baseWidget.py +++ b/kivyblocks/baseWidget.py @@ -170,9 +170,13 @@ class Text(Label): if self.bgcolor: self.color = self.bgcolor - def resize(self, size): + def resize(self, *args): if not self.size_hint_y: - self.width = size[0] + ps = [0,0,0,0] + if hasattr(self.parent,'padding'): + ps = self.parent.padding + + self.width = self.parent.width - ps[0] - ps[2] self.set_widget_height() def set_widget_height(self, *args): diff --git a/kivyblocks/bgcolorbehavior.py b/kivyblocks/bgcolorbehavior.py index 64cc593..d852c55 100644 --- a/kivyblocks/bgcolorbehavior.py +++ b/kivyblocks/bgcolorbehavior.py @@ -1,31 +1,29 @@ from kivy.logger import Logger from kivy.graphics import Color, Rectangle, RoundedRectangle -from kivy.properties import ListProperty +from kivy.properties import ListProperty, NumericProperty from .color_definitions import getColors _logcnt = 0 class BGColorBehavior(object): - def __init__(self, color_level=-1,radius=[],**kwargs): - if color_level==-1: - color_level = 0 - self.color_level = color_level + color_level=NumericProperty(0) + radius = ListProperty([]) + def __init__(self, **kwargs): self.bg_ready = False - self.radius = radius self.bgcolor = [] self.fgcolor = [] - fg,bg= getColors(color_level) + fg,bg= getColors(self.color_level) self.fgcolor = fg self.bgcolor = bg self.normal_bgcolor = bg self.normal_fgcolor = fg - fg,bg= getColors(color_level,selected=True) + fg,bg= getColors(self.color_level,selected=True) self.selected_bgcolor = bg self.selected_fgcolor = fg # self.on_bgcolor() self.bind(size=self.onSize_bgcolor_behavior, pos=self.onSize_bgcolor_behavior) - self.bind(children=self.on_bgcolor) + # self.bind(children=self.on_bgcolor) def onSize_bgcolor_behavior(self,o,v=None): if not hasattr(self,'rect'): diff --git a/kivyblocks/blocksapp.py b/kivyblocks/blocksapp.py index 494f323..716a9c2 100644 --- a/kivyblocks/blocksapp.py +++ b/kivyblocks/blocksapp.py @@ -42,6 +42,12 @@ class ServerInfo: self.publickey = None self.authCode = None + def realurl(url): + if url.startswith('https://') or url.startswith('http://'): + return url + config = getConfig() + return '%s%s' % (config.uihome, url) + def getServerPK(self): config = getConfig() url = '%s%s' % (config.uihome, config.publickey_url) diff --git a/kivyblocks/markdown.py b/kivyblocks/markdown.py index 77f7081..1a91457 100644 --- a/kivyblocks/markdown.py +++ b/kivyblocks/markdown.py @@ -2,9 +2,11 @@ from kivy.properties import StringProperty, NumericProperty, ListProperty from kivy.factory import Factory from kivy.clock import Clock from kivy.logger import Logger +from kivy.uix.scrollview import ScrollView +from kivy.uix.label import Label from kivy.uix.image import AsyncImage -from .baseWidget import ScrollWidget, getDataHandler, CSize +from .baseWidget import ScrollWidget, getDataHandler, VBox from .utils import CSize import re @@ -16,7 +18,7 @@ LINK_LEAD = "[" REFER_SPLIT = "](" REFER_END = ")" -class MarkDownParser(object): +class MarkdownParser(object): mdkeys = [ SOURCE_DELIMITER, VIDEO_LEAD, @@ -34,10 +36,13 @@ class MarkDownParser(object): IMAGE_LEAD:self.lead_image, } - def handle_source_delimiter(self,mdtext): + def handle_source_delimiter(self): x = self.mdtext.split(SOURCE_DELIMITER,1) - mk_p = MarkDownParser(x[0]) - self.result += mk_p.parse() + mk_p = MarkdownParser(x[0]) + d = { + 'source':x[0] + } + self.result.append(d) if len(x) > 1: self.mdtext = x[1] @@ -67,12 +72,12 @@ class MarkDownParser(object): def lead_audio(self): x = self.mdtext.split(REFER_SPLIT,1) if len(x)<2: - self.text = f'{VIDEO_LEAD}' + self.text = f'{AUDIO_LEAD}' return title = x[0] y = x[1].split(REFER_END,1) if len(y) < 2: - self.text = f'{VIDEO_LEAD}' + self.text = f'{AUDIO_LEAD}' return url = y[0] d = { @@ -88,12 +93,12 @@ class MarkDownParser(object): def lead_image(self): x = self.mdtext.split(REFER_SPLIT,1) if len(x)<2: - self.text = f'{VIDEO_LEAD}' + self.text = f'{IMAGE_LEAD}' return title = x[0] y = x[1].split(REFER_END,1) if len(y) < 2: - self.text = f'{VIDEO_LEAD}' + self.text = f'{IMAGE_LEAD}' return url = y[0] d = { @@ -144,39 +149,37 @@ class MDImage(AsyncImage): def image_loaded(self,o, *args): self.image_size = self.texture.size - def resize(self, size): - self.parent_width = size[0] - Logger.info('MDImage: resize called, size=%s', size) + def resize(self, *args): + ps = [0,0,0,0] + if hasattr(self.parent, 'padding'): + ps = self.parent.padding + self.parent_width = self.parent.width - ps[0] - ps[2] + if self.texture: + if self.texture.size: + self.image_size = self.texture.size + Logger.info('MDImage:resize called, %d', self.parent_width) def on_image_size(self, *args): if self.parent_width: - self.set_new_height() + Logger.info('MDImage:on_iage_size %d, %s', self.parent_width, + self.image_size) + self.set_image_size() def on_parent_width(self, *args): if self.image_size: - self.set_new_height() + Logger.info('MDImage:on_oarent_width %d, %s', self.parent_width, + self.image_size) + self.set_image_size() - def set_new_height(self): + def set_image_size(self): + self.width = self.parent_width self.height = self.parent_width * self.image_size[1] \ / self.image_size[0] - Logger.info('MDImage: set_new_height called, size=%s', self.size) - + Logger.info('MDImage:set_image_size %d, %d', self.width, + self.height) -class Markdown(ScrollWidget): - """ -# Markdown -MArkdown widget using to render a markdown file and show it -description file format -{ - "widgettype":"Markdown", - "options":{ - "source": the markdown file - other options - } -} - """ - source = StringProperty(None) - def __init__(self, **kw): +class MarkdownBody(VBox): + def __init__(self, md_obj=None, padding=[10,10,10,10], **kw): self.part_handlers = { "pure_md":self.build_pure_md, "source":self.build_source, @@ -184,14 +187,45 @@ description file format "video":self.build_video, "audio":self.build_audio } - ScrollWidget.__init__(self, **kw) - self.bind(source=self.load_text) - if self.source: - Clock.schedule_once(self.load_text, 0.3) - self.bind(size=self.setChildMinWidth) + self.md_obj = md_obj + super().__init__(**kw) + self.padding=padding + self.size_hint = None,None + self.bind(parent=self.resize) + self.resize() + + def show_mdtext(self, mdtext): + mdp = MarkdownParser(mdtext) + parts = mdp.parse() + self.clear_widgets() + for p in parts: + for k,v in p.items(): + f = self.part_handlers.get(k) + f(v) + + def resize(self, *args): + Logger.info('MDBody:resize called') + if self.parent: + ps = [0,0,0,0] + if hasattr(self.parent, 'padding'): + ps = self.parent.padding + h = 0 + for c in self.children: + if hasattr(c, 'resize'): + c.resize() + h += c.height + self.width = self.parent.width - ps[0] - ps[2] + self.height = h + else: + Logger.info('resize:parent is null') def build_source(self,source_desc): - pass + color_level = self.color_level+1 + w = MarkdownBody(md_obj=self.md_obj, + color_level=color_level,size_hint_y=None) + w.show_mdtext(source_desc) + self.add_widget(w) + w.resize() def build_pure_md(self, mdtext): for l in mdtext.split('\n'): @@ -206,7 +240,7 @@ description file format ) self.add_widget(w) - w.resize(self.size) + w.resize() def build_video(self, video_desc): w = Factory.NewVideo(source=video_desc['url'], @@ -225,45 +259,13 @@ description file format w.bind(on_enter_focus=f1) w.bind(on_leave_focus=f2) self.add_widget(w) - w.resize(self.size) + w.resize() def build_audio(self, audio_desc): w = Factory.APlayer(source=audio_desc.url) w.bind(minimum_height=w.setter('height')) self.add_widget(w) - def setChildMinWidth(self, *args): - print('size changed') - for i,c in enumerate(self._inner.children): - c.width = self.width - if hasattr(c, 'resize'): - c.resize(self.size) - - def load_text(self, *args): - print('source fired, hahaha', *args) - self.clear_widgets() - h = getDataHandler(self.source) - h.bind(on_success=self.update) - h.bind(on_error=self.show_error) - h.handle() - - def show_error(self, o, e): - print('load_text(), error=',e) - - def update(self, o, text): - print('text=',text, type(text)) - if not text: - return - text = ''.join(text.split('\r')) - mdp = MarkDownParser(text) - parts = mdp.parse() - for p in parts: - for k,v in p.items(): - print('part=', k, v) - f = self.part_handlers.get(k) - f(v) - - def parse_title(self, txt, level): w = Factory.Blocks().widgetBuild({ "widgettype":f"Title{level}", @@ -285,8 +287,7 @@ description file format if h1 is None or h1 < clen: h1 = clen w.height = h1 - w.bind(on_ref_press=self.open_new_md) - # w.bind(minimum_height=w.setter('height')) + w.bind(on_ref_press=self.md_obj.open_new_md) self.add_widget(w) def parse_line(self, l): @@ -328,14 +329,9 @@ description file format if h is None or h < clen: h = clen w.height = h - w.bind(on_ref_press=self.open_new_md) - # w.bind(minimum_height=w.setter('height')) + w.bind(on_ref_press=self.md_obj.open_new_md) self.add_widget(w) - def open_new_md(self, o, value): - print(value,'is it a link') - self.source = value - def mktext2bbtext(self,mdtext): """ convert markdown text to bbtag text kivy Label Widget recognized @@ -379,3 +375,63 @@ description file format mdtext) return mdtext + + +class Markdown(ScrollView): + """ +# Markdown +MArkdown widget using to render a markdown file and show it +description file format +{ + "widgettype":"Markdown", + "options":{ + "source": the markdown file + other options + } +} + """ + source = StringProperty(None) + + def __init__(self, color_level=0, **kw): + self.color_level = color_level + ScrollView.__init__(self, **kw) + self.root_body = MarkdownBody(md_obj=self, + color_level=color_level, + size_hint_y=None + ) + self.root_body.bind(minimum_height=self.root_body.setter('height')) + self.add_widget(self.root_body) + if self.source: + self.load_text() + self.bind(size=self.root_body.resize) + self.bind(source=self.load_text) + # Clock.schedule_interval(self.check_parent_window, 1) + + def check_parent_window(self, *args): + pw = self.get_parent_window() + rw = self.get_root_window() + print('MD: pw=%s,rw=%s', pw,rw) + + def stop_media(self, *args): + self.root_body.stp_video() + + def load_text(self, *args): + h = getDataHandler(self.source) + h.bind(on_success=self.update) + h.bind(on_error=self.show_error) + h.handle() + + def show_error(self, o, e): + print('load_text(), error=',e) + + def update(self, o, text): + print('text=',text, type(text)) + if not text: + return + text = ''.join(text.split('\r')) + self.root_body.show_mdtext(text) + self.scroll_y = 1 + + def open_new_md(self, o, value): + print(value,'is it a link') + self.source = value diff --git a/kivyblocks/newvideo.py b/kivyblocks/newvideo.py index 9ae945f..09bd84b 100644 --- a/kivyblocks/newvideo.py +++ b/kivyblocks/newvideo.py @@ -29,18 +29,23 @@ class NewVideo(BGColorBehavior, Video): self.register_event_type('on_enter_focus') self.register_event_type('on_load_success') self.bind(source=self.record_start_time) + self.bind(loaded=self.on_video_loaded) self.load_status = None Clock.schedule_interval(self.check_focus,0.2) def on_enter_focus(self, v=None): - Logger.info('NewView: enter focus') + Logger.info('NewVideo: enter focus') def on_leave_focus(self, v=None): - Logger.info('NewView: leave focus') + Logger.info('NewVideo: leave focus') def check_focus(self,*args): if not self.parent: + self.center_screen = False return + if not self.get_root_window(): + self.center_screen = False + w = self.parent pos = w.to_widget(*Window.center) if self.collide_point(*pos): @@ -54,39 +59,39 @@ class NewVideo(BGColorBehavior, Video): else: self.dispatch('on_leave_focus') - def resize(self, size): + def resize(self, *args): v_size = self.get_video_size() if v_size: self.height = self.width * v_size[1] / v_size[0] def on_load_success(self, *args): + Logger.info('NewVideo: pn_load_success fired, %s', args) pass def record_start_time(self, *args): self.start_time = time.time() self.load_status = 'start' + Logger.info('NewVideo: record_start_time() called %s',self.load_status) def on_open_failed(self, source, x=None): Logger.error(f'NewVideo: source={self.source} open failed') self.load_status = 'failed' def ffplayerLog(self, msg, level): - """ if 'Connection to tcp' in msg and 'failed' in msg: - self.dispatch('on_open_failed', self.source) + self.dispatch('on_open_failed', msg) if 'Invalid data found when processing input' in msg: - self.dispatch('on_open_failed', self.source) + self.dispatch('on_open_failed', msg) + if 'Http error 403' in msg: + self.dispatch('on_open_failed', msg) if 'End of file' in msg: - self.dispatch('on_open_failed', self.source) + self.dispatch('on_open_failed', msg) if 'I/O error' in msg: - self.dispatch('on_open_failed', self.source) - if 'Server returned 404 Not Found' in msg: - self.dispatch('on_open_failed', self.source) - """ + self.dispatch('on_open_failed', msg) if 'Server returned 4' in msg: - self.dispatch('on_open_failed', self.source) + self.dispatch('on_open_failed', msg) if 'Server returned 5' in msg: - self.dispatch('on_open_failed', self.source) + self.dispatch('on_open_failed', msg) msg = msg.strip() if msg: @@ -108,18 +113,22 @@ class NewVideo(BGColorBehavior, Video): else: Logger.error('NewVideo: _video has not _ffplayer, do nothong') + def on_video_loaded(self, *args): + Logger.info('NewVideo: status=%s, load_status=%s', self.state, + self.load_status) + if self.load_status == 'start': + self.load_status = 'success' + t = time.time() + self.dispatch('on_load_success', + { + 'source':self.source, + 'time':int((t - self.start_time)*100) + }) + + def on_state(self,*args): super().on_state(*args) if self.state == 'play': - if self.load_status == 'start': - self.load_status = 'success' - t = time.time() - self.dispatch('on_load_success', - { - 'source':self.source, - 'time':int((t - self.start_time)*100) - }) - Window.allow_screensaver = False else: Window.allow_screensaver = True diff --git a/kivyblocks/widgetExt/messager.py b/kivyblocks/widgetExt/messager.py index 3918dd5..3e0f8bc 100644 --- a/kivyblocks/widgetExt/messager.py +++ b/kivyblocks/widgetExt/messager.py @@ -1,27 +1,39 @@ from kivy.uix.popup import Popup +from kivy.clock import Clock from kivy.uix.boxlayout import BoxLayout from kivy.uix.textinput import TextInput from appPublic.Singleton import SingletonDecorator @SingletonDecorator class Messager: - def __init__(self): + def __init__(self, show_time=0): + self.show_time = show_time + self.time_task = None self.w = Popup(content=BoxLayout(orientation='vertical'), title="Error info",size_hint=(0.8,0.8)) self.messager = TextInput(size=self.w.content.size, multiline=True,readonly=True) self.w.content.add_widget(self.messager) + def set_time_task(self): + if self.show_time: + self.time_task = Clock.schedule_once(self.hide, + self.show_time) def show_error(self,e): self.w.title = "error" self.messager.text = str(e) self.w.open() + self.set_time_task() def show_info(self,info): self.w.title = "info" self.messager.text = str(info) self.w.open() + self.set_time_task() - def hide_error(self): + def hide(self, *args): + if self.time_task: + self.time_task.cancel() + self.time_task = None self.w.dismiss()