This commit is contained in:
yumoqing 2021-03-05 17:15:51 +08:00
parent 4a9de787c7
commit 3580406e13
6 changed files with 200 additions and 115 deletions

View File

@ -170,9 +170,13 @@ class Text(Label):
if self.bgcolor: if self.bgcolor:
self.color = self.bgcolor self.color = self.bgcolor
def resize(self, size): def resize(self, *args):
if not self.size_hint_y: 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() self.set_widget_height()
def set_widget_height(self, *args): def set_widget_height(self, *args):

View File

@ -1,31 +1,29 @@
from kivy.logger import Logger from kivy.logger import Logger
from kivy.graphics import Color, Rectangle, RoundedRectangle from kivy.graphics import Color, Rectangle, RoundedRectangle
from kivy.properties import ListProperty from kivy.properties import ListProperty, NumericProperty
from .color_definitions import getColors from .color_definitions import getColors
_logcnt = 0 _logcnt = 0
class BGColorBehavior(object): class BGColorBehavior(object):
def __init__(self, color_level=-1,radius=[],**kwargs): color_level=NumericProperty(0)
if color_level==-1: radius = ListProperty([])
color_level = 0 def __init__(self, **kwargs):
self.color_level = color_level
self.bg_ready = False self.bg_ready = False
self.radius = radius
self.bgcolor = [] self.bgcolor = []
self.fgcolor = [] self.fgcolor = []
fg,bg= getColors(color_level) fg,bg= getColors(self.color_level)
self.fgcolor = fg self.fgcolor = fg
self.bgcolor = bg self.bgcolor = bg
self.normal_bgcolor = bg self.normal_bgcolor = bg
self.normal_fgcolor = fg 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_bgcolor = bg
self.selected_fgcolor = fg self.selected_fgcolor = fg
# self.on_bgcolor() # self.on_bgcolor()
self.bind(size=self.onSize_bgcolor_behavior, self.bind(size=self.onSize_bgcolor_behavior,
pos=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): def onSize_bgcolor_behavior(self,o,v=None):
if not hasattr(self,'rect'): if not hasattr(self,'rect'):

View File

@ -42,6 +42,12 @@ class ServerInfo:
self.publickey = None self.publickey = None
self.authCode = 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): def getServerPK(self):
config = getConfig() config = getConfig()
url = '%s%s' % (config.uihome, config.publickey_url) url = '%s%s' % (config.uihome, config.publickey_url)

View File

@ -2,9 +2,11 @@ from kivy.properties import StringProperty, NumericProperty, ListProperty
from kivy.factory import Factory from kivy.factory import Factory
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger from kivy.logger import Logger
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.image import AsyncImage from kivy.uix.image import AsyncImage
from .baseWidget import ScrollWidget, getDataHandler, CSize from .baseWidget import ScrollWidget, getDataHandler, VBox
from .utils import CSize from .utils import CSize
import re import re
@ -16,7 +18,7 @@ LINK_LEAD = "["
REFER_SPLIT = "](" REFER_SPLIT = "]("
REFER_END = ")" REFER_END = ")"
class MarkDownParser(object): class MarkdownParser(object):
mdkeys = [ mdkeys = [
SOURCE_DELIMITER, SOURCE_DELIMITER,
VIDEO_LEAD, VIDEO_LEAD,
@ -34,10 +36,13 @@ class MarkDownParser(object):
IMAGE_LEAD:self.lead_image, IMAGE_LEAD:self.lead_image,
} }
def handle_source_delimiter(self,mdtext): def handle_source_delimiter(self):
x = self.mdtext.split(SOURCE_DELIMITER,1) x = self.mdtext.split(SOURCE_DELIMITER,1)
mk_p = MarkDownParser(x[0]) mk_p = MarkdownParser(x[0])
self.result += mk_p.parse() d = {
'source':x[0]
}
self.result.append(d)
if len(x) > 1: if len(x) > 1:
self.mdtext = x[1] self.mdtext = x[1]
@ -67,12 +72,12 @@ class MarkDownParser(object):
def lead_audio(self): def lead_audio(self):
x = self.mdtext.split(REFER_SPLIT,1) x = self.mdtext.split(REFER_SPLIT,1)
if len(x)<2: if len(x)<2:
self.text = f'{VIDEO_LEAD}' self.text = f'{AUDIO_LEAD}'
return return
title = x[0] title = x[0]
y = x[1].split(REFER_END,1) y = x[1].split(REFER_END,1)
if len(y) < 2: if len(y) < 2:
self.text = f'{VIDEO_LEAD}' self.text = f'{AUDIO_LEAD}'
return return
url = y[0] url = y[0]
d = { d = {
@ -88,12 +93,12 @@ class MarkDownParser(object):
def lead_image(self): def lead_image(self):
x = self.mdtext.split(REFER_SPLIT,1) x = self.mdtext.split(REFER_SPLIT,1)
if len(x)<2: if len(x)<2:
self.text = f'{VIDEO_LEAD}' self.text = f'{IMAGE_LEAD}'
return return
title = x[0] title = x[0]
y = x[1].split(REFER_END,1) y = x[1].split(REFER_END,1)
if len(y) < 2: if len(y) < 2:
self.text = f'{VIDEO_LEAD}' self.text = f'{IMAGE_LEAD}'
return return
url = y[0] url = y[0]
d = { d = {
@ -144,39 +149,37 @@ class MDImage(AsyncImage):
def image_loaded(self,o, *args): def image_loaded(self,o, *args):
self.image_size = self.texture.size self.image_size = self.texture.size
def resize(self, size): def resize(self, *args):
self.parent_width = size[0] ps = [0,0,0,0]
Logger.info('MDImage: resize called, size=%s', size) 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): def on_image_size(self, *args):
if self.parent_width: 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): def on_parent_width(self, *args):
if self.image_size: 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.height = self.parent_width * self.image_size[1] \
/ self.image_size[0] / 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): class MarkdownBody(VBox):
""" def __init__(self, md_obj=None, padding=[10,10,10,10], **kw):
# 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):
self.part_handlers = { self.part_handlers = {
"pure_md":self.build_pure_md, "pure_md":self.build_pure_md,
"source":self.build_source, "source":self.build_source,
@ -184,14 +187,45 @@ description file format
"video":self.build_video, "video":self.build_video,
"audio":self.build_audio "audio":self.build_audio
} }
ScrollWidget.__init__(self, **kw) self.md_obj = md_obj
self.bind(source=self.load_text) super().__init__(**kw)
if self.source: self.padding=padding
Clock.schedule_once(self.load_text, 0.3) self.size_hint = None,None
self.bind(size=self.setChildMinWidth) 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): 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): def build_pure_md(self, mdtext):
for l in mdtext.split('\n'): for l in mdtext.split('\n'):
@ -206,7 +240,7 @@ description file format
) )
self.add_widget(w) self.add_widget(w)
w.resize(self.size) w.resize()
def build_video(self, video_desc): def build_video(self, video_desc):
w = Factory.NewVideo(source=video_desc['url'], w = Factory.NewVideo(source=video_desc['url'],
@ -225,45 +259,13 @@ description file format
w.bind(on_enter_focus=f1) w.bind(on_enter_focus=f1)
w.bind(on_leave_focus=f2) w.bind(on_leave_focus=f2)
self.add_widget(w) self.add_widget(w)
w.resize(self.size) w.resize()
def build_audio(self, audio_desc): def build_audio(self, audio_desc):
w = Factory.APlayer(source=audio_desc.url) w = Factory.APlayer(source=audio_desc.url)
w.bind(minimum_height=w.setter('height')) w.bind(minimum_height=w.setter('height'))
self.add_widget(w) 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): def parse_title(self, txt, level):
w = Factory.Blocks().widgetBuild({ w = Factory.Blocks().widgetBuild({
"widgettype":f"Title{level}", "widgettype":f"Title{level}",
@ -285,8 +287,7 @@ description file format
if h1 is None or h1 < clen: if h1 is None or h1 < clen:
h1 = clen h1 = clen
w.height = h1 w.height = h1
w.bind(on_ref_press=self.open_new_md) w.bind(on_ref_press=self.md_obj.open_new_md)
# w.bind(minimum_height=w.setter('height'))
self.add_widget(w) self.add_widget(w)
def parse_line(self, l): def parse_line(self, l):
@ -328,14 +329,9 @@ description file format
if h is None or h < clen: if h is None or h < clen:
h = clen h = clen
w.height = h w.height = h
w.bind(on_ref_press=self.open_new_md) w.bind(on_ref_press=self.md_obj.open_new_md)
# w.bind(minimum_height=w.setter('height'))
self.add_widget(w) self.add_widget(w)
def open_new_md(self, o, value):
print(value,'is it a link')
self.source = value
def mktext2bbtext(self,mdtext): def mktext2bbtext(self,mdtext):
""" """
convert markdown text to bbtag text kivy Label Widget recognized convert markdown text to bbtag text kivy Label Widget recognized
@ -379,3 +375,63 @@ description file format
mdtext) mdtext)
return 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

View File

@ -29,18 +29,23 @@ class NewVideo(BGColorBehavior, Video):
self.register_event_type('on_enter_focus') self.register_event_type('on_enter_focus')
self.register_event_type('on_load_success') self.register_event_type('on_load_success')
self.bind(source=self.record_start_time) self.bind(source=self.record_start_time)
self.bind(loaded=self.on_video_loaded)
self.load_status = None self.load_status = None
Clock.schedule_interval(self.check_focus,0.2) Clock.schedule_interval(self.check_focus,0.2)
def on_enter_focus(self, v=None): def on_enter_focus(self, v=None):
Logger.info('NewView: enter focus') Logger.info('NewVideo: enter focus')
def on_leave_focus(self, v=None): def on_leave_focus(self, v=None):
Logger.info('NewView: leave focus') Logger.info('NewVideo: leave focus')
def check_focus(self,*args): def check_focus(self,*args):
if not self.parent: if not self.parent:
self.center_screen = False
return return
if not self.get_root_window():
self.center_screen = False
w = self.parent w = self.parent
pos = w.to_widget(*Window.center) pos = w.to_widget(*Window.center)
if self.collide_point(*pos): if self.collide_point(*pos):
@ -54,39 +59,39 @@ class NewVideo(BGColorBehavior, Video):
else: else:
self.dispatch('on_leave_focus') self.dispatch('on_leave_focus')
def resize(self, size): def resize(self, *args):
v_size = self.get_video_size() v_size = self.get_video_size()
if v_size: if v_size:
self.height = self.width * v_size[1] / v_size[0] self.height = self.width * v_size[1] / v_size[0]
def on_load_success(self, *args): def on_load_success(self, *args):
Logger.info('NewVideo: pn_load_success fired, %s', args)
pass pass
def record_start_time(self, *args): def record_start_time(self, *args):
self.start_time = time.time() self.start_time = time.time()
self.load_status = 'start' self.load_status = 'start'
Logger.info('NewVideo: record_start_time() called %s',self.load_status)
def on_open_failed(self, source, x=None): def on_open_failed(self, source, x=None):
Logger.error(f'NewVideo: source={self.source} open failed') Logger.error(f'NewVideo: source={self.source} open failed')
self.load_status = 'failed' self.load_status = 'failed'
def ffplayerLog(self, msg, level): def ffplayerLog(self, msg, level):
"""
if 'Connection to tcp' in msg and 'failed' in msg: 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: 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: 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: if 'I/O error' in msg:
self.dispatch('on_open_failed', self.source) self.dispatch('on_open_failed', msg)
if 'Server returned 404 Not Found' in msg:
self.dispatch('on_open_failed', self.source)
"""
if 'Server returned 4' in 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: if 'Server returned 5' in msg:
self.dispatch('on_open_failed', self.source) self.dispatch('on_open_failed', msg)
msg = msg.strip() msg = msg.strip()
if msg: if msg:
@ -108,18 +113,22 @@ class NewVideo(BGColorBehavior, Video):
else: else:
Logger.error('NewVideo: _video has not _ffplayer, do nothong') 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): def on_state(self,*args):
super().on_state(*args) super().on_state(*args)
if self.state == 'play': 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 Window.allow_screensaver = False
else: else:
Window.allow_screensaver = True Window.allow_screensaver = True

View File

@ -1,27 +1,39 @@
from kivy.uix.popup import Popup from kivy.uix.popup import Popup
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput from kivy.uix.textinput import TextInput
from appPublic.Singleton import SingletonDecorator from appPublic.Singleton import SingletonDecorator
@SingletonDecorator @SingletonDecorator
class Messager: 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'), self.w = Popup(content=BoxLayout(orientation='vertical'),
title="Error info",size_hint=(0.8,0.8)) title="Error info",size_hint=(0.8,0.8))
self.messager = TextInput(size=self.w.content.size, self.messager = TextInput(size=self.w.content.size,
multiline=True,readonly=True) multiline=True,readonly=True)
self.w.content.add_widget(self.messager) 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): def show_error(self,e):
self.w.title = "error" self.w.title = "error"
self.messager.text = str(e) self.messager.text = str(e)
self.w.open() self.w.open()
self.set_time_task()
def show_info(self,info): def show_info(self,info):
self.w.title = "info" self.w.title = "info"
self.messager.text = str(info) self.messager.text = str(info)
self.w.open() 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() self.w.dismiss()