Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to user less/precompiled themes #82

Merged
merged 11 commits into from
Dec 14, 2016
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,18 @@ usage: jt [-h] [-l] [-t THEME] [-f MONOFONT] [-fs MONOSIZE] [-nf NBFONT]
```sh
# list available themes
# oceans16 | grade3 | chesterish | onedork
# available themes are marked with keywords:
# user - global - precompiled
# user themes are stored in:
# ~/.jupyter-themes/styles
# themes available in precompiled (css) form only are stored in:
# ~/.jupyter-themes/styles/compiled
jt -l

# select theme...
# note that customization options have no effect on precompiled themes
# available themes are searched in the following order:
# user > user precompiled > global > global precompiled
jt -t chesterish

# toggle toolbar ON and notebook name ON
Expand Down
54 changes: 49 additions & 5 deletions jupyterthemes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os, sys
from argparse import ArgumentParser
from glob import glob
from collections import namedtuple
modules = glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules]

Expand All @@ -17,11 +18,44 @@
# path to local site-packages/jupyterthemes
package_dir = os.path.dirname(os.path.realpath(__file__))

# path to user jupyter-themes dir
user_dir = os.path.join(os.path.expanduser('~'), '.jupyter-themes')

def get_themes():
""" return list of available themes """
styles_dir = os.path.join(package_dir, 'styles')
themes = [os.path.basename(theme).replace('.less', '')
for theme in glob('{0}/*.less'.format(styles_dir))]
precompiled_dir = os.path.join(package_dir, 'styles', 'compiled')
styles_dir_user = os.path.join(user_dir, 'styles')
precompiled_dir_user = os.path.join(user_dir, 'styles', 'compiled')
Theme = namedtuple('Theme', ('name', 'tags'))
themes = []
names = []

if os.path.isdir(styles_dir_user):
for theme in glob('{0}/*.less'.format(styles_dir_user)):
name = name=os.path.basename(theme).replace('.less', '')
themes.append(Theme(name=name, tags=['user']))
names.append(name)

if os.path.isdir(precompiled_dir_user):
for theme in glob('{0}/*.css'.format(precompiled_dir_user)):
name = name=os.path.basename(theme).replace('.css', '')
if name in names: continue
themes.append(Theme(name=name, tags=['user', 'precompiled']))
names.append(name)

for theme in glob('{0}/*.less'.format(styles_dir)):
name = os.path.basename(theme).replace('.less', '')
if name in names: continue
themes.append(Theme(name=name, tags=['global']))
names.append(name)

for theme in glob('{0}/*.css'.format(precompiled_dir)):
name = os.path.basename(theme).replace('.css', '')
if name in names: continue
themes.append(Theme(name=name, tags=['global', 'precompiled']))
names.append(name)

return themes

def install_theme(theme, monofont='droidmono', monosize=11, nbfont='exosans', nbfontsize=13, tcfont='loraserif', tcfontsize=13, margins='auto', cellwidth=980, lineheight=170, cursorwidth=2, cursorcolor='default', altlayout=False, altprompt=False, hideprompt=False, vimext=False, toolbar=False, nbname=False):
Expand All @@ -31,6 +65,13 @@ def install_theme(theme, monofont='droidmono', monosize=11, nbfont='exosans', nb
from jupyterthemes import stylefx
stylefx.reset_default(False)
stylefx.check_directories()
themes = get_themes()

for theme_ in themes:
if theme_.name == theme and 'precompiled' in theme_.tags:
stylefx.install_precompiled_theme(theme)
return

# initialize style_less & style_css
style_less = stylefx.set_font_properties(monofont=monofont, monosize=monosize, nbfont=nbfont, nbfontsize=nbfontsize, tcfont=tcfont, tcfontsize=tcfontsize)
# define some vars for cell layout
Expand Down Expand Up @@ -68,10 +109,13 @@ def main():
parser.add_argument('-vim', "--vimext", action='store_true', default=False, help="toggle styles for vim")
parser.add_argument('-r', "--reset", action='store_true', help="reset to default theme")
args = parser.parse_args()
themes = get_themes()
say_themes = "Available Themes: \n {}".format('\n '.join(themes))
themes = sorted(get_themes(), key=lambda t: t.name)
maxlen = max(len(theme.name) for theme in themes)
theme_listing = ['{} {}({})'.format(theme.name, (maxlen-len(theme.name))*' ', ', '.join(theme.tags))
for theme in themes]
say_themes = "Available Themes: \n {}" .format('\n '.join(theme_listing))
if args.theme:
if args.theme not in themes:
if args.theme not in (theme.name for theme in themes):
print("Didn't recognize theme name: {}".format(args.theme))
print(say_themes)
exit(1)
Expand Down
21 changes: 19 additions & 2 deletions jupyterthemes/stylefx.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# path to local site-packages/jupyterthemes
package_dir = os.path.dirname(os.path.realpath(__file__))

# path to user jupyter-themes dir
user_dir = os.path.join(os.path.expanduser('~'), '.jupyter-themes')

# path to save tempfile with style_less before reading/compiling
tempfile = os.path.join(package_dir, 'tempfile.less')
vimtemp = os.path.join(package_dir, 'vimtemp.less')
Expand All @@ -24,6 +27,7 @@
# theme colors, layout, and font directories
layouts_dir = os.path.join(package_dir, 'layout')
styles_dir = os.path.join(package_dir, 'styles')
styles_dir_user = os.path.join(user_dir, 'styles')
fonts_dir = os.path.join(package_dir, 'fonts')

# layout files for notebook, codemirror, cells, mathjax, & vim ext
Expand Down Expand Up @@ -69,7 +73,12 @@ def remove_temp_file():
def install_precompiled_theme(theme):
# for Python 3.5, install selected theme from precompiled defaults
compiled_dir = os.path.join(styles_dir, 'compiled')
theme_src = os.path.join(compiled_dir, '{}.css'.format(theme))
compiled_dir_user = os.path.join(styles_dir_user, 'compiled')
if (os.path.isdir(compiled_dir_user) and
'{}.css'.format(theme) in os.listdir(compiled_dir_user)):
theme_src = os.path.join(compiled_dir_user, '{}.css'.format(theme))
else:
theme_src = os.path.join(compiled_dir, '{}.css'.format(theme))
theme_dst = os.path.join(jupyter_custom, 'custom.css')
copyfile(theme_src, theme_dst)
for fontcode in ['exosans', 'loraserif', 'droidmono', 'firacode']:
Expand Down Expand Up @@ -166,7 +175,15 @@ def style_layout(style_less, theme='grade3', cursorwidth=2, cursorcolor='default
# grade3's altlayout is reverse of default
if theme=='grade3':
altlayout = not altlayout
style_less += '@import "styles{}";\n'.format(''.join([os.sep, theme]))

if (os.path.isdir(styles_dir_user) and
'{}.less'.format(theme) in os.listdir(styles_dir_user)):
theme_relpath = os.path.relpath(os.path.join(styles_dir_user, theme), package_dir)
else:
theme_relpath = os.path.relpath(os.path.join(styles_dir, theme), package_dir)

style_less += '@import "{}";\n'.format(theme_relpath)

textcell_bg = '@cc-input-bg'
promptText = '@input-prompt'
promptBG = '@cc-input-bg'
Expand Down