88from os import path , environ
99
1010from tornado import template
11+ from functools import lru_cache
1112
1213from ..__version__ import __version__ as version
1314from ..exceptions import PyWebIOWarning
1415from ..utils import isgeneratorfunction , iscoroutinefunction , get_function_name , get_function_doc , \
15- get_function_attr
16+ get_function_attr , STATIC_PATH
1617
1718"""
1819The maximum size in bytes of a http request body or a websocket message, after which the request or websocket is aborted
2324
2425DEFAULT_CDN = "https://cdn.jsdelivr.net/gh/wang0618/PyWebIO-assets@v{version}/"
2526
26- BOOTSTRAP_VERSION = '4.4.1'
27-
2827_global_config = {'title' : 'PyWebIO Application' }
29- config_keys = ['title' , 'description' , 'js_file' , 'js_code' , 'css_style' , 'css_file' ]
28+ config_keys = ['title' , 'description' , 'js_file' , 'js_code' , 'css_style' , 'css_file' , 'theme' ]
3029AppMeta = namedtuple ('App' , config_keys )
3130
3231_here_dir = path .dirname (path .abspath (__file__ ))
@@ -44,35 +43,30 @@ def render_page(app, protocol, cdn):
4443 assert protocol in ('ws' , 'http' )
4544 meta = parse_app_metadata (app )
4645 if cdn is True :
47- cdn = DEFAULT_CDN .format (version = version )
46+ base_url = DEFAULT_CDN .format (version = version )
4847 elif not cdn :
49- cdn = ''
48+ base_url = ''
5049 else : # user custom cdn
51- cdn = cdn .rstrip ('/' ) + '/'
50+ base_url = cdn .rstrip ('/' ) + '/'
5251
53- bootstrap_css = bootstrap_css_url ()
52+ theme = environ .get ('PYWEBIO_THEME' , meta .theme )
53+ check_theme (theme )
5454
5555 return _index_page_tpl .generate (title = meta .title , description = meta .description , protocol = protocol ,
56- script = True , content = '' , base_url = cdn , bootstrap_css = bootstrap_css ,
56+ script = True , content = '' , base_url = base_url ,
5757 js_file = meta .js_file or [], js_code = meta .js_code , css_style = meta .css_style ,
58- css_file = meta .css_file or [])
59-
58+ css_file = meta .css_file or [], theme = theme )
6059
61- def bootstrap_css_url ():
62- """Get bootstrap theme css url from environment variable PYWEBIO_THEME
63-
64- PYWEBIO_THEME can be one of bootswatch themes, or a custom css url.
65- """
66- theme_name = environ .get ('PYWEBIO_THEME' )
67- bootswatch_themes = {'flatly' , 'yeti' , 'cerulean' , 'pulse' , 'journal' , 'cosmo' , 'sandstone' , 'simplex' , 'minty' ,
68- 'slate' , 'superhero' , 'lumen' , 'spacelab' , 'materia' , 'litera' , 'sketchy' , 'cyborg' , 'solar' ,
69- 'lux' , 'united' , 'darkly' }
7060
71- if theme_name in bootswatch_themes :
72- return 'https://cdn.jsdelivr.net/npm/bootswatch@{version}/dist/{theme}/bootstrap.min.css' .format (
73- version = BOOTSTRAP_VERSION , theme = theme_name )
61+ @lru_cache (maxsize = 64 )
62+ def check_theme (theme ):
63+ """check theme file existence"""
64+ if not theme :
65+ return
7466
75- return theme_name # it's a url
67+ theme_file = path .join (STATIC_PATH , 'css' , 'bs-theme' , theme + '.min.css' )
68+ if not path .isfile (theme_file ):
69+ raise RuntimeError ("Can't find css file for theme `%s`" % theme )
7670
7771
7872def cdn_validation (cdn , level = 'warn' , stacklevel = 3 ):
@@ -325,11 +319,19 @@ def hello():
325319 return config (title = title , description = description )
326320
327321
328- def config (* , title = None , description = None , js_code = None , js_file = [], css_style = None , css_file = []):
322+ def config (* , title = None , description = None , theme = None , js_code = None , js_file = [], css_style = None , css_file = []):
329323 """PyWebIO application configuration
330324
331325 :param str title: Application title
332326 :param str description: Application description
327+ :param str theme: Application theme. Available themes are: ``dark``, ``sketchy``, ``lux``.
328+ You can also use environment variable ``PYWEBIO_THEME`` to specify the theme (with high priority).
329+
330+ .. collapse:: Open Source Credits
331+
332+ The dark theme is modified from ForEvolve's `bootstrap-dark <https://github.com/ForEvolve/bootstrap-dark>`_.
333+ The rest of the themes are from `bootswatch <https://bootswatch.com/4/>`_.
334+
333335 :param str js_code: The javascript code that you want to inject to page.
334336 :param str/list js_file: The javascript files that inject to page, can be a URL in str or a list of it.
335337 :param str css_style: The CSS style that you want to inject to page.
@@ -365,6 +367,9 @@ def app():
365367 pass
366368
367369 .. versionadded:: 1.4
370+
371+ .. versionchanged:: 1.5
372+ add ``theme`` parameter
368373 """
369374 if isinstance (js_file , str ):
370375 js_file = [js_file ]
0 commit comments