import os
import json
import warnings
from inspect import currentframe
from sympy.external import import_module
appdirs = import_module(
'appdirs',
min_module_version='1.4.4')
appname = "spb"
cfg_file = "config.json"
cfg_dir = appdirs.user_data_dir(appname)
os.makedirs(cfg_dir, exist_ok=True)
file_path = os.path.join(cfg_dir, cfg_file)
def _hardcoded_defaults():
# Hardcoded default values
return dict(
plotly={
# More themes at: https://plotly.com/python/templates/
"theme": "seaborn",
# Show/hide main grid
"grid": True,
# Render latex with Plotly
"use_latex": False,
# automatically compute new data when zoom/pan are used
"update_event": False,
},
bokeh={
# More themes at:
# https://docs.bokeh.org/en/latest/docs/reference/themes.html
"theme": "caliber",
# How will the plot resizes to fill the available space.
"sizing_mode": "fixed",
# the default size of the plot when sizing_mode=fixed
"width": 600,
"height": 400,
# Show/hide main grid
"grid": True,
# Show/hide minor grid
"show_minor_grid": True,
# Depending on the used Bokeh `themes`, probably need
# to adjust the opacity of the minor grid lines
"minor_grid_line_alpha": 0.6,
# Controls the spacing of the dashes in minor grid lines
"minor_grid_line_dash": [2, 2],
# Render latex with Bokeh
"use_latex": False,
# automatically compute new data when zoom/pan are used
"update_event": False,
},
k3d={
# Background color
"bg_color": 0xFFFFFF, # 3620427
# Grid color
"grid_color": 0xE6E6E6, # 0x888888
# Color of the labels
"label_color": 0x444444, # 0xDDDDDD
# Show/hide main grid
"grid": True,
# Render latex with K3D
"use_latex": True,
# how the scene rotates when click+drag
# Possible values: "trackball", "orbit", "fly". If None, it
# defaults to "trackball"
"camera_mode": None,
},
matplotlib={
# Position of the intersection of the axis. If None, use a
# standard Matplotlib layout with vertical axis on the left,
# horizontal axis on the bottom.
# Possible values: "auto", "center", None
"axis_center": None,
# Show/hide main grid
"grid": True,
# Show/hide minor grid
"show_minor_grid": False,
# Render latex with Matplotlib
"use_latex": True,
# automatically compute new data when zoom/pan are used
"update_event": False,
},
mayavi={
"size": (800, 500),
"bg_color": None,
"fg_color": None,
# Show/hide main grid
"grid": True,
# Render latex with Mayavi
"use_latex": True,
},
# Possible values: "matplotlib", "plotly", "bokeh"
backend_2D="matplotlib",
# Possible values: "matplotlib", "plotly", "k3d"
backend_3D="matplotlib",
# settings about the spb.ccomplex.complex module
complex={
"modules": None, # None (default to Numpy/Scipy), "mpmath"
"coloring": "a" # read plot_complex docs for more options
},
# settings about interactive-widget plots
interactive={
# set the default interactive module. Possible options:
# "ipywidgets" or "panel"
"module": "ipywidgets",
# Render latex on the widget's labels
"use_latex": True,
# Controls wether sliders trigger the update at each
# tick (value False) or only when the mouse click is released
# (value True). Only works with module="panel".
"throttled": False,
# If True, the interactive application will be served on a new
# browser window, otherwise it will be shown on Jupyter Notebook.
# Only works with module="panel".
"servable": False,
# If the interactive application is being served to a new
# browser window, an appropriate theme can be choosed.
# Possible values: "dark", "light". Only works with module="panel".
"theme": "light"
},
plot3d={
# Wheter to use a color map on a 3D surface
"use_cm": False
},
bode={
"phase_unit": "rad",
"freq_unit": "rad/sec"
},
# settings that will be passed to the adaptive library:
# https://github.com/python-adaptive/adaptive/
adaptive={
# set True for adaptive algorithm, or False for uniform meshing
# algorithm for line plots
"used_by_default": False,
# higher number produces coarser results
"goal": 0.01
},
plot_range={
# set the default plot range
"min": -10,
"max": 10
}
)
[docs]
def get_default_settings():
"""Return the default setting dictionary for inspection.
Examples
========
Visualize the default settings.
>>> from spb.defaults import get_default_settings
>>> print(get_default_settings) # doctest: +SKIP
"""
return _hardcoded_defaults()
[docs]
def reset():
"""Restore original settings."""
set_defaults(_hardcoded_defaults())
def _load_settings():
"""Load settings and inject the names into the current namespace."""
mergedeep = import_module('mergedeep')
merge = mergedeep.merge
frame = currentframe()
cfg = dict()
if os.path.exists(file_path):
with open(file_path) as f:
cfg = json.load(f)
default_cfg = _hardcoded_defaults()
# Because the user can directly change the configuration file, we need
# to assure that all the necessary options are present (maybe, the user
# deleted something accidentally)
cfg = merge({}, default_cfg, cfg)
frame.f_globals["cfg"] = cfg
# check that the chosen backends are available
backends_2D = ["plotly", "bokeh", "matplotlib"]
backends_3D = ["plotly", "matplotlib", "k3d"]
def check_backend(k, backends):
if cfg[k] not in backends:
# restore hardcoded values in order to be able to load the module
# the next time
reset()
raise ValueError(
"`{}` must be one of the following ".format(k)
+ "values: {}\n".format(backends)
+ "Received: = '{}'\n".format(cfg[k])
+ "Reset config file to hardcoded default values: done."
)
check_backend("backend_2D", backends_2D)
check_backend("backend_3D", backends_3D)
# load the selected backends
if cfg["backend_2D"] == "plotly":
from spb.backends.plotly import PlotlyBackend as TWO_D_B
elif cfg["backend_2D"] == "bokeh":
from spb.backends.bokeh import BokehBackend as TWO_D_B
elif cfg["backend_2D"] == "matplotlib":
from spb.backends.matplotlib import MatplotlibBackend as TWO_D_B
elif cfg["backend_2D"] == "k3d":
from spb.backends.k3d import K3DBackend as TWO_D_B
if cfg["backend_2D"] == cfg["backend_3D"]:
THREE_D_B = TWO_D_B
else:
if cfg["backend_3D"] == "plotly":
from spb.backends.plotly import PlotlyBackend as THREE_D_B
elif cfg["backend_3D"] == "matplotlib":
from spb.backends.matplotlib import MatplotlibBackend as THREE_D_B
elif cfg["backend_3D"] == "k3d":
from spb.backends.k3d import K3DBackend as THREE_D_B
frame.f_globals["TWO_D_B"] = TWO_D_B
frame.f_globals["THREE_D_B"] = THREE_D_B
[docs]
def set_defaults(cfg):
"""Set the default options for the plotting backends and save them to
a file.
Parameters
==========
cfg : dict
Dictionary containing the new values
Examples
========
Change the default 2D plotting backend to MatplotlibBackend.
>>> from spb.defaults import cfg, set_defaults
>>> ## to visualize the current settings
>>> # print(cfg)
>>> cfg["backend_2D"] = "matplotlib"
>>> set_defaults(cfg)
Notes
=====
This plotting module uses the `appdir` module [#fn1]_ to determine the
best location where to save the settings. It will save a human readable
`config.json` file, which SHOULD NOT be modified directly with a text
editor.
Use the ``set_defaults`` function to modify the configuration settings!
References
==========
.. [#fn1] https://github.com/ActiveState/appdirs
"""
with open(file_path, "w", encoding="utf-8") as f:
json.dump(cfg, f, ensure_ascii=False, indent=4)
warnings.warn("Successfully written settings to {}".format(file_path))
_load_settings()
_load_settings()