lets-plot 4.8.1rc1__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lets_plot/__init__.py +382 -0
- lets_plot/_global_settings.py +192 -0
- lets_plot/_kbridge.py +197 -0
- lets_plot/_type_utils.py +133 -0
- lets_plot/_version.py +6 -0
- lets_plot/bistro/__init__.py +16 -0
- lets_plot/bistro/_plot2d_common.py +106 -0
- lets_plot/bistro/corr.py +448 -0
- lets_plot/bistro/im.py +196 -0
- lets_plot/bistro/joint.py +192 -0
- lets_plot/bistro/qq.py +207 -0
- lets_plot/bistro/residual.py +341 -0
- lets_plot/bistro/waterfall.py +332 -0
- lets_plot/export/__init__.py +6 -0
- lets_plot/export/ggsave_.py +172 -0
- lets_plot/frontend_context/__init__.py +8 -0
- lets_plot/frontend_context/_configuration.py +140 -0
- lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
- lets_plot/frontend_context/_frontend_ctx.py +16 -0
- lets_plot/frontend_context/_html_contexts.py +223 -0
- lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
- lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
- lets_plot/frontend_context/_json_contexts.py +39 -0
- lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
- lets_plot/frontend_context/_mime_types.py +7 -0
- lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
- lets_plot/frontend_context/_static_svg_ctx.py +26 -0
- lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
- lets_plot/frontend_context/sandbox.py +5 -0
- lets_plot/geo_data/__init__.py +19 -0
- lets_plot/geo_data/core.py +335 -0
- lets_plot/geo_data/geocoder.py +988 -0
- lets_plot/geo_data/geocodes.py +512 -0
- lets_plot/geo_data/gis/__init__.py +0 -0
- lets_plot/geo_data/gis/fluent_dict.py +201 -0
- lets_plot/geo_data/gis/geocoding_service.py +42 -0
- lets_plot/geo_data/gis/geometry.py +91 -0
- lets_plot/geo_data/gis/json_request.py +232 -0
- lets_plot/geo_data/gis/json_response.py +308 -0
- lets_plot/geo_data/gis/request.py +492 -0
- lets_plot/geo_data/gis/response.py +247 -0
- lets_plot/geo_data/livemap_helper.py +65 -0
- lets_plot/geo_data/to_geo_data_frame.py +141 -0
- lets_plot/geo_data/type_assertion.py +34 -0
- lets_plot/geo_data_internals/__init__.py +4 -0
- lets_plot/geo_data_internals/constants.py +13 -0
- lets_plot/geo_data_internals/utils.py +33 -0
- lets_plot/mapping.py +115 -0
- lets_plot/package_data/lets-plot.min.js +3 -0
- lets_plot/plot/__init__.py +64 -0
- lets_plot/plot/_global_theme.py +14 -0
- lets_plot/plot/annotation.py +290 -0
- lets_plot/plot/coord.py +242 -0
- lets_plot/plot/core.py +1071 -0
- lets_plot/plot/expand_limits_.py +78 -0
- lets_plot/plot/facet.py +210 -0
- lets_plot/plot/font_features.py +71 -0
- lets_plot/plot/geom.py +9146 -0
- lets_plot/plot/geom_extras.py +53 -0
- lets_plot/plot/geom_function_.py +219 -0
- lets_plot/plot/geom_imshow_.py +393 -0
- lets_plot/plot/geom_livemap_.py +343 -0
- lets_plot/plot/ggbunch_.py +96 -0
- lets_plot/plot/gggrid_.py +139 -0
- lets_plot/plot/ggtb_.py +81 -0
- lets_plot/plot/guide.py +231 -0
- lets_plot/plot/label.py +187 -0
- lets_plot/plot/marginal_layer.py +181 -0
- lets_plot/plot/plot.py +245 -0
- lets_plot/plot/pos.py +344 -0
- lets_plot/plot/sampling.py +338 -0
- lets_plot/plot/sandbox_.py +26 -0
- lets_plot/plot/scale.py +3580 -0
- lets_plot/plot/scale_colormap_mpl.py +300 -0
- lets_plot/plot/scale_convenience.py +155 -0
- lets_plot/plot/scale_identity_.py +653 -0
- lets_plot/plot/scale_position.py +1342 -0
- lets_plot/plot/series_meta.py +209 -0
- lets_plot/plot/stat.py +585 -0
- lets_plot/plot/subplots.py +331 -0
- lets_plot/plot/subplots_util.py +24 -0
- lets_plot/plot/theme_.py +790 -0
- lets_plot/plot/theme_set.py +418 -0
- lets_plot/plot/tooltip.py +486 -0
- lets_plot/plot/util.py +267 -0
- lets_plot/settings_utils.py +244 -0
- lets_plot/tilesets.py +429 -0
- lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
- lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
- lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
- lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
- lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
- lets_plot_kotlin_bridge.cpython-311-x86_64-linux-gnu.so +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Copyright (c) 2020. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from os.path import join
|
|
6
|
+
from typing import Union, Optional
|
|
7
|
+
|
|
8
|
+
from ..plot.core import PlotSpec
|
|
9
|
+
from ..plot.core import _to_svg, _to_html, _to_mvg, _export_as_raster
|
|
10
|
+
from ..plot.plot import GGBunch
|
|
11
|
+
from ..plot.subplots import SupPlotsSpec
|
|
12
|
+
|
|
13
|
+
__all__ = ['ggsave']
|
|
14
|
+
|
|
15
|
+
_DEF_EXPORT_DIR = "lets-plot-images"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ggsave(plot: Union[PlotSpec, SupPlotsSpec, GGBunch], filename: str, *, path: str = None, iframe: bool = True,
|
|
19
|
+
scale: float = None, w: Optional[float] = None, h: Optional[float] = None, unit: Optional[str] = None,
|
|
20
|
+
dpi: Optional[int] = None) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Export plot to a file.
|
|
23
|
+
Supported formats: PNG, SVG, PDF, HTML.
|
|
24
|
+
|
|
25
|
+
The exported file is created in the directory ${user.dir}/lets-plot-images
|
|
26
|
+
if not specified otherwise (see the ``path`` parameter).
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
plot : ``PlotSpec``
|
|
31
|
+
Plot specification to export.
|
|
32
|
+
filename : str
|
|
33
|
+
Name of the file. It must end with a file extension corresponding
|
|
34
|
+
to one of the supported formats: SVG, HTML (or HTM), PNG, PDF (requires the pillow library).
|
|
35
|
+
path : str
|
|
36
|
+
Path to a directory to save image files in.
|
|
37
|
+
By default, it is ${user.dir}/lets-plot-images.
|
|
38
|
+
iframe : bool, default=True
|
|
39
|
+
Whether to wrap the HTML page into an iFrame.
|
|
40
|
+
Only applicable when exporting to HTML.
|
|
41
|
+
Some browsers may not display some UTF-8 characters correctly when setting iframe=True
|
|
42
|
+
scale : float, default=2.0
|
|
43
|
+
Scaling factor for raster output.
|
|
44
|
+
Only applicable when exporting to PNG or PDF.
|
|
45
|
+
w : float, default=None
|
|
46
|
+
Width of the output image in units.
|
|
47
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
48
|
+
h : float, default=None
|
|
49
|
+
Height of the output image in units.
|
|
50
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
51
|
+
unit : {'in', 'cm', 'mm', 'px'}, default='in'
|
|
52
|
+
Unit of the output image. One of: 'in', 'cm', 'mm' or 'px'.
|
|
53
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
54
|
+
dpi : int, default=300
|
|
55
|
+
Resolution in dots per inch.
|
|
56
|
+
Only applicable when exporting to PNG or PDF.
|
|
57
|
+
The default value depends on the unit:
|
|
58
|
+
|
|
59
|
+
- for 'px' it is 96 (output image will have the same pixel size as ``w``, and ``h`` values)
|
|
60
|
+
- for physical units ('in', 'cm', 'mm') it is 300.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
str
|
|
65
|
+
Absolute pathname of the created file.
|
|
66
|
+
|
|
67
|
+
Notes
|
|
68
|
+
-----
|
|
69
|
+
Large plot dimensions without units require explicit unit specification.
|
|
70
|
+
When ``w`` or ``h`` value exceeds 20 without specifying units (e.g., ``ggsave(p, 300, 400)``),
|
|
71
|
+
we ask to specify units explicitly:
|
|
72
|
+
``ggsave(p, 300, 400, unit='px')`` or ``ggsave(p, 3, 4, unit='in')``.
|
|
73
|
+
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
The output format is inferred from the filename extension.
|
|
77
|
+
|
|
78
|
+
For PNG, and PDF formats:
|
|
79
|
+
|
|
80
|
+
- If ``w``, ``h``, ``unit``, and ``dpi`` are all specified:
|
|
81
|
+
|
|
82
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is ignored.
|
|
83
|
+
- The output size is calculated using the specified ``w``, ``h``, ``unit`` and ``dpi``.
|
|
84
|
+
|
|
85
|
+
- The plot is resized to fit the specified ``w`` x ``h`` area, which may affect the layout, tick labels, and other elements.
|
|
86
|
+
|
|
87
|
+
- If only ``dpi`` is specified:
|
|
88
|
+
|
|
89
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is converted to inches using the standard display PPI of 96.
|
|
90
|
+
- The output size is then calculated based on the specified DPI.
|
|
91
|
+
|
|
92
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
93
|
+
- Useful for printing - the plot will appear nearly the same size as on screen.
|
|
94
|
+
|
|
95
|
+
- If ``w``, ``h`` are not specified:
|
|
96
|
+
|
|
97
|
+
- The ``scale`` parameter is used to determine the output size.
|
|
98
|
+
|
|
99
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
100
|
+
- Useful for generating high-resolution images suitable for publication.
|
|
101
|
+
|
|
102
|
+
For SVG format:
|
|
103
|
+
|
|
104
|
+
- If ``w``, ``h``, and ``unit`` are specified:
|
|
105
|
+
|
|
106
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is ignored.
|
|
107
|
+
- The output size is calculated using the specified ``w``, ``h``, and ``unit``.
|
|
108
|
+
|
|
109
|
+
----
|
|
110
|
+
|
|
111
|
+
Plots with ``geom_livemap()`` can be saved to HTML only.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
--------
|
|
116
|
+
.. jupyter-execute::
|
|
117
|
+
:linenos:
|
|
118
|
+
:emphasize-lines: 6
|
|
119
|
+
|
|
120
|
+
from IPython.display import Image
|
|
121
|
+
from lets_plot import *
|
|
122
|
+
LetsPlot.setup_html()
|
|
123
|
+
filename = 'plot.png'
|
|
124
|
+
plot = ggplot() + geom_point(x=0, y=0) + ggtitle(filename)
|
|
125
|
+
fullpath = ggsave(plot, filename, w=4, h=3)
|
|
126
|
+
Image(filename=fullpath, width=600, height=450)
|
|
127
|
+
|
|
128
|
+
|
|
|
129
|
+
|
|
130
|
+
.. jupyter-execute::
|
|
131
|
+
:linenos:
|
|
132
|
+
:emphasize-lines: 6
|
|
133
|
+
|
|
134
|
+
from IPython.display import HTML
|
|
135
|
+
from lets_plot import *
|
|
136
|
+
LetsPlot.setup_html()
|
|
137
|
+
filename = 'plot.html'
|
|
138
|
+
plot = ggplot() + geom_point(x=0, y=0) + ggtitle(filename)
|
|
139
|
+
fullpath = ggsave(plot, filename, iframe=False)
|
|
140
|
+
HTML(filename=fullpath)
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if not (isinstance(plot, PlotSpec) or isinstance(plot, SupPlotsSpec) or isinstance(plot, GGBunch)):
|
|
145
|
+
raise ValueError("PlotSpec, SupPlotsSpec or GGBunch expected but was: {}".format(type(plot)))
|
|
146
|
+
|
|
147
|
+
filename = filename.strip()
|
|
148
|
+
name, ext = os.path.splitext(filename)
|
|
149
|
+
|
|
150
|
+
if not name:
|
|
151
|
+
raise ValueError("Malformed filename: '{}'.".format(filename))
|
|
152
|
+
if not ext:
|
|
153
|
+
raise ValueError("Missing file extension: '{}'.".format(filename))
|
|
154
|
+
|
|
155
|
+
if not path:
|
|
156
|
+
path = join(os.getcwd(), _DEF_EXPORT_DIR)
|
|
157
|
+
|
|
158
|
+
pathname = join(path, filename)
|
|
159
|
+
|
|
160
|
+
ext = ext[1:].lower()
|
|
161
|
+
if ext == 'svg':
|
|
162
|
+
return _to_svg(plot, pathname, w=w, h=h, unit=unit)
|
|
163
|
+
elif ext in ['html', 'htm']:
|
|
164
|
+
return _to_html(plot, pathname, iframe=iframe)
|
|
165
|
+
elif ext in ['png', 'pdf']:
|
|
166
|
+
return _export_as_raster(plot, pathname, scale, export_format=ext, w=w, h=h, unit=unit, dpi=dpi)
|
|
167
|
+
elif ext == 'mvg':
|
|
168
|
+
return _to_mvg(plot, pathname, scale, w=w, h=h, unit=unit, dpi=dpi)
|
|
169
|
+
else:
|
|
170
|
+
raise ValueError(
|
|
171
|
+
"Unsupported file extension: '{}'\nPlease use one of: 'png', 'svg', 'pdf', 'html', 'htm'".format(ext)
|
|
172
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
|
|
7
|
+
from ._frontend_ctx import FrontendContext
|
|
8
|
+
from ._html_contexts import _create_html_frontend_context, _create_wb_html_frontend_context
|
|
9
|
+
from ._json_contexts import _create_json_frontend_context, _is_Intellij_Python_Lets_Plot_Plugin
|
|
10
|
+
from ._mime_types import TEXT_HTML, LETS_PLOT_JSON
|
|
11
|
+
from ._static_svg_ctx import StaticSvgImageContext
|
|
12
|
+
from ._webbr_html_page_ctx import WebBrHtmlPageContext
|
|
13
|
+
from .._version import __version__
|
|
14
|
+
from ..plot.core import PlotSpec
|
|
15
|
+
from ..plot.plot import GGBunch
|
|
16
|
+
from ..plot.subplots import SupPlotsSpec
|
|
17
|
+
|
|
18
|
+
__all__ = []
|
|
19
|
+
|
|
20
|
+
_frontend_contexts: Dict[str, FrontendContext] = {}
|
|
21
|
+
|
|
22
|
+
_default_mimetype = TEXT_HTML
|
|
23
|
+
if _is_Intellij_Python_Lets_Plot_Plugin():
|
|
24
|
+
_default_mimetype = LETS_PLOT_JSON
|
|
25
|
+
_frontend_contexts[LETS_PLOT_JSON] = _create_json_frontend_context()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _setup_html_context(*,
|
|
29
|
+
isolated_frame: bool = None,
|
|
30
|
+
offline: bool,
|
|
31
|
+
no_js: bool,
|
|
32
|
+
show_status: bool,
|
|
33
|
+
dev_options: Dict = None) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Configures Lets-Plot HTML output.
|
|
36
|
+
See the docstring in `setup_html()` for details on parameters.
|
|
37
|
+
"""
|
|
38
|
+
global _default_mimetype
|
|
39
|
+
if _default_mimetype == LETS_PLOT_JSON:
|
|
40
|
+
# Plots will be rendered by Lets-Plot IntelliJ plugin.
|
|
41
|
+
# But still create HTML context in case it's necessary.
|
|
42
|
+
if show_status:
|
|
43
|
+
print(
|
|
44
|
+
'Lets-Plot v{}: output mimetype {} configured by default. No need for HTML output.'.format(__version__,
|
|
45
|
+
LETS_PLOT_JSON))
|
|
46
|
+
|
|
47
|
+
if no_js:
|
|
48
|
+
ctx = StaticSvgImageContext()
|
|
49
|
+
else:
|
|
50
|
+
ctx = _create_html_frontend_context(isolated_frame, offline=offline, dev_options=dev_options)
|
|
51
|
+
|
|
52
|
+
ctx.configure(verbose=show_status)
|
|
53
|
+
_frontend_contexts[TEXT_HTML] = ctx
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _setup_wb_html_context(*,
|
|
57
|
+
exec: str,
|
|
58
|
+
new: bool) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Configures Lets-Plot HTML output for showing in a browser.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
exec : str, optional
|
|
65
|
+
Command to execute to open the plot in a web browser.
|
|
66
|
+
If not specified, the default browser will be used.
|
|
67
|
+
new : bool, default=False
|
|
68
|
+
If True, the URL is opened in a new window of the web browser.
|
|
69
|
+
If False, the URL is opened in the already opened web browser window.
|
|
70
|
+
"""
|
|
71
|
+
ctx = _create_wb_html_frontend_context(exec, new)
|
|
72
|
+
_frontend_contexts[TEXT_HTML] = ctx
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _display_plot(spec: Any):
|
|
76
|
+
"""
|
|
77
|
+
Draw plot or `bunch` of plots in the current frontend context
|
|
78
|
+
:param spec: PlotSpec or GGBunch object
|
|
79
|
+
"""
|
|
80
|
+
if not (isinstance(spec, PlotSpec) or isinstance(spec, SupPlotsSpec) or isinstance(spec, GGBunch)):
|
|
81
|
+
raise ValueError("PlotSpec or SupPlotsSpec expected but was: {}".format(type(spec)))
|
|
82
|
+
|
|
83
|
+
if _default_mimetype == TEXT_HTML:
|
|
84
|
+
ctx = _frontend_contexts.get(TEXT_HTML)
|
|
85
|
+
|
|
86
|
+
if ctx is not None and isinstance(ctx, WebBrHtmlPageContext):
|
|
87
|
+
ctx.show(spec.as_dict())
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
# If ctx is None, _as_html() will try to initialize the context lazily
|
|
91
|
+
plot_html = _as_html(spec.as_dict())
|
|
92
|
+
try:
|
|
93
|
+
from IPython.display import display_html
|
|
94
|
+
display_html(plot_html, raw=True)
|
|
95
|
+
return
|
|
96
|
+
except ImportError:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
print(spec.as_dict())
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
if _default_mimetype == LETS_PLOT_JSON:
|
|
103
|
+
_frontend_contexts[LETS_PLOT_JSON].show(spec.as_dict())
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
# fallback to plain text.
|
|
107
|
+
print(spec.as_dict())
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _as_html(plot_spec: Dict) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Creates plot HTML using 'html' frontend context.
|
|
113
|
+
|
|
114
|
+
:param plot_spec: dict
|
|
115
|
+
"""
|
|
116
|
+
ctx = _frontend_contexts.get(TEXT_HTML)
|
|
117
|
+
|
|
118
|
+
if ctx is None:
|
|
119
|
+
# Set up HTML context lazily
|
|
120
|
+
_setup_html_context(isolated_frame=None,
|
|
121
|
+
offline=False,
|
|
122
|
+
no_js=False,
|
|
123
|
+
show_status=False)
|
|
124
|
+
|
|
125
|
+
ctx = _frontend_contexts[TEXT_HTML]
|
|
126
|
+
|
|
127
|
+
# Check if this context allows lazy set-up.
|
|
128
|
+
requires_configure = getattr(ctx, 'requires_configure', False)
|
|
129
|
+
if requires_configure:
|
|
130
|
+
# Clear the context and return an error message
|
|
131
|
+
_frontend_contexts[TEXT_HTML] = None
|
|
132
|
+
return """\
|
|
133
|
+
<div style="color:darkred;">
|
|
134
|
+
Lets-plot `html` is not configured.<br>
|
|
135
|
+
Try to use `LetsPlot.setup_html()` before first occurrence of plot.
|
|
136
|
+
</div>
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
# Generate "display" HTML
|
|
140
|
+
return ctx.as_str(plot_spec)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
import os
|
|
6
|
+
import pkgutil
|
|
7
|
+
import random
|
|
8
|
+
import string
|
|
9
|
+
|
|
10
|
+
from .._global_settings import get_js_cdn_url
|
|
11
|
+
|
|
12
|
+
# Data-attributes used to store extra information about the meaning of 'script' elements
|
|
13
|
+
_ATT_SCRIPT_KIND = 'data-lets-plot-script'
|
|
14
|
+
_SCRIPT_KIND_LIB_LOADING = 'library'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_dynamic_configure_html(offline: bool, verbose: bool) -> str:
|
|
18
|
+
"""
|
|
19
|
+
Generate HTML for dynamic loading of lets-plot.js library.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
offline : bool
|
|
24
|
+
If True, embeds the JS library directly. If False, loads from CDN.
|
|
25
|
+
verbose : bool
|
|
26
|
+
If True, shows success/error messages to the user.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
str
|
|
31
|
+
HTML string that loads the lets-plot.js library and sets up the dynamic loading mechanism.
|
|
32
|
+
"""
|
|
33
|
+
if offline:
|
|
34
|
+
return _configure_embedded_script(verbose)
|
|
35
|
+
else:
|
|
36
|
+
return _configure_connected_script(verbose)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _configure_connected_script(verbose: bool) -> str:
|
|
40
|
+
"""Generate HTML that loads lets-plot.js from CDN."""
|
|
41
|
+
url = get_js_cdn_url()
|
|
42
|
+
output_id = _rand_string()
|
|
43
|
+
success_message = """
|
|
44
|
+
var div = document.createElement("div");
|
|
45
|
+
div.style.color = 'darkblue';
|
|
46
|
+
div.textContent = 'Lets-Plot JS successfully loaded.';
|
|
47
|
+
document.getElementById("{id}").appendChild(div);
|
|
48
|
+
""".format(id=output_id) if verbose else ""
|
|
49
|
+
|
|
50
|
+
return """
|
|
51
|
+
<div id="{id}"></div>
|
|
52
|
+
<script type="text/javascript" {data_attr}="{script_kind}">
|
|
53
|
+
if(!window.letsPlotCallQueue) {{
|
|
54
|
+
window.letsPlotCallQueue = [];
|
|
55
|
+
}};
|
|
56
|
+
window.letsPlotCall = function(f) {{
|
|
57
|
+
window.letsPlotCallQueue.push(f);
|
|
58
|
+
}};
|
|
59
|
+
(function() {{
|
|
60
|
+
var script = document.createElement("script");
|
|
61
|
+
script.type = "text/javascript";
|
|
62
|
+
script.src = "{url}";
|
|
63
|
+
script.onload = function() {{
|
|
64
|
+
window.letsPlotCall = function(f) {{f();}};
|
|
65
|
+
window.letsPlotCallQueue.forEach(function(f) {{f();}});
|
|
66
|
+
window.letsPlotCallQueue = [];
|
|
67
|
+
{success_message}
|
|
68
|
+
}};
|
|
69
|
+
script.onerror = function(event) {{
|
|
70
|
+
window.letsPlotCall = function(f) {{}}; // noop
|
|
71
|
+
window.letsPlotCallQueue = [];
|
|
72
|
+
var div = document.createElement("div");
|
|
73
|
+
div.style.color = 'darkred';
|
|
74
|
+
div.textContent = 'Error loading Lets-Plot JS';
|
|
75
|
+
document.getElementById("{id}").appendChild(div);
|
|
76
|
+
}};
|
|
77
|
+
var e = document.getElementById("{id}");
|
|
78
|
+
e.appendChild(script);
|
|
79
|
+
}})()
|
|
80
|
+
</script>
|
|
81
|
+
""".format(
|
|
82
|
+
data_attr=_ATT_SCRIPT_KIND,
|
|
83
|
+
script_kind=_SCRIPT_KIND_LIB_LOADING,
|
|
84
|
+
id=output_id,
|
|
85
|
+
url=url,
|
|
86
|
+
success_message=success_message)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _configure_embedded_script(verbose: bool) -> str:
|
|
90
|
+
"""Generate HTML that embeds lets-plot.js directly."""
|
|
91
|
+
js_name = "lets-plot.min.js"
|
|
92
|
+
path = os.path.join("package_data", js_name)
|
|
93
|
+
js_code = pkgutil.get_data("lets_plot", path).decode("utf-8")
|
|
94
|
+
success_message = '<div style="color:darkblue;">Lets-Plot JS is embedded.</div>' if verbose else ""
|
|
95
|
+
|
|
96
|
+
return """
|
|
97
|
+
<script type="text/javascript" {data_attr}="{script_kind}">
|
|
98
|
+
window.letsPlotCall = function(f) {{f();}};
|
|
99
|
+
console.log('Embedding: {js_name}');
|
|
100
|
+
{js_code}
|
|
101
|
+
</script>
|
|
102
|
+
{success_message}
|
|
103
|
+
""".format(
|
|
104
|
+
data_attr=_ATT_SCRIPT_KIND,
|
|
105
|
+
script_kind=_SCRIPT_KIND_LIB_LOADING,
|
|
106
|
+
js_code=js_code,
|
|
107
|
+
js_name=js_name,
|
|
108
|
+
success_message=success_message)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _rand_string(size=6) -> str:
|
|
112
|
+
"""Generate a random string for unique element IDs."""
|
|
113
|
+
alphabet = string.ascii_letters + string.digits
|
|
114
|
+
# noinspection PyShadowingBuiltins
|
|
115
|
+
return ''.join([random.choice(alphabet) for _ in range(size)])
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FrontendContext:
|
|
9
|
+
def configure(self, verbose: bool):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def as_str(self, plot_spec: Dict) -> str:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def show(self, plot_spec: Dict) -> str:
|
|
16
|
+
pass
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Copyright (c) 2020. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from ._frontend_ctx import FrontendContext
|
|
7
|
+
from ._isolated_webview_panel_ctx import IsolatedWebviewPanelContext
|
|
8
|
+
from ._jupyter_notebook_ctx import JupyterNotebookContext
|
|
9
|
+
from ._static_html_page_ctx import StaticHtmlPageContext
|
|
10
|
+
from ._webbr_html_page_ctx import WebBrHtmlPageContext
|
|
11
|
+
from .._global_settings import has_global_value, get_global_bool, HTML_ISOLATED_FRAME
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _create_html_frontend_context(
|
|
15
|
+
isolated_frame: bool = None,
|
|
16
|
+
offline: bool = None,
|
|
17
|
+
dev_options: dict = None) -> FrontendContext:
|
|
18
|
+
"""
|
|
19
|
+
Configures Lets-Plot HTML output.
|
|
20
|
+
See the docstring in `setup_html()` for details on parameters.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
if dev_options is None:
|
|
24
|
+
dev_options = {}
|
|
25
|
+
else:
|
|
26
|
+
dev_options = dev_options.copy()
|
|
27
|
+
|
|
28
|
+
# Extract and remove isolated_webview_panel from dev_options
|
|
29
|
+
isolated_webview_panel = dev_options.pop('isolated_webview_panel', None)
|
|
30
|
+
|
|
31
|
+
if isolated_webview_panel is None:
|
|
32
|
+
isolated_webview_panel = _is_positron_console()
|
|
33
|
+
|
|
34
|
+
if isolated_webview_panel:
|
|
35
|
+
return IsolatedWebviewPanelContext(offline, **dev_options)
|
|
36
|
+
|
|
37
|
+
if isolated_frame is None:
|
|
38
|
+
isolated_frame = _use_isolated_frame()
|
|
39
|
+
|
|
40
|
+
if isolated_frame:
|
|
41
|
+
return StaticHtmlPageContext(offline, **dev_options)
|
|
42
|
+
else:
|
|
43
|
+
return JupyterNotebookContext(offline, **dev_options)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _create_wb_html_frontend_context(exec: str, new: bool) -> FrontendContext:
|
|
47
|
+
"""
|
|
48
|
+
Configures Lets-Plot HTML output for showing in a web browser.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
exec : str, optional
|
|
53
|
+
The name of the web browser to use.
|
|
54
|
+
If not specified, the default browser will be used.
|
|
55
|
+
new : bool, default=False
|
|
56
|
+
If True, the URL is opened in a new window of the web browser.
|
|
57
|
+
If False, the URL is opened in the already opened web browser window.
|
|
58
|
+
"""
|
|
59
|
+
return WebBrHtmlPageContext(exec, new)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _use_isolated_frame() -> bool:
|
|
63
|
+
# check environment
|
|
64
|
+
if has_global_value(HTML_ISOLATED_FRAME):
|
|
65
|
+
return get_global_bool(HTML_ISOLATED_FRAME)
|
|
66
|
+
|
|
67
|
+
# return _detect_isolated_frame()
|
|
68
|
+
if not _is_IPython_display():
|
|
69
|
+
return True # fallback to a complete HTML page in the output
|
|
70
|
+
|
|
71
|
+
# Some notebooks behave like a single HTML page where the JS library is loaded once per notebook.
|
|
72
|
+
if (_is_jupyter_classic() or
|
|
73
|
+
_is_kaggle() or
|
|
74
|
+
_is_positron_notebook()):
|
|
75
|
+
return False # no iframes in the output cell
|
|
76
|
+
|
|
77
|
+
# Most online notebook platforms are showing cell output in iframe and require
|
|
78
|
+
# a complete HTML page in the output which includes both:
|
|
79
|
+
# - the script loading JS library and
|
|
80
|
+
# - the script that uses this JS lib to create a plot.
|
|
81
|
+
|
|
82
|
+
if (_is_google_colab() or
|
|
83
|
+
_is_azure_notebook() or
|
|
84
|
+
_is_deepnote() or
|
|
85
|
+
_is_databricks() or
|
|
86
|
+
_is_nextjournal()
|
|
87
|
+
):
|
|
88
|
+
return True # complete HTML page in the output
|
|
89
|
+
|
|
90
|
+
# if os.getenv("PLOTLY_RENDERER") == "colab":
|
|
91
|
+
# # good enough - something colab-like
|
|
92
|
+
# return True # Colab -> iframe
|
|
93
|
+
|
|
94
|
+
# try:
|
|
95
|
+
# shell = get_ipython().__class__.__name__
|
|
96
|
+
# if shell == 'ZMQInteractiveShell':
|
|
97
|
+
# return False # Jupyter notebook or qtconsole -> load JS librarty once per notebook
|
|
98
|
+
# elif shell == 'TerminalInteractiveShell':
|
|
99
|
+
# return True # Terminal running IPython -> an isolated HTML page to show somehow
|
|
100
|
+
# else:
|
|
101
|
+
# return True # Other type (?)
|
|
102
|
+
# except NameError:
|
|
103
|
+
# return True # some other env (even standard Python interpreter) -> an isolated HTML page to show somehow
|
|
104
|
+
|
|
105
|
+
# Fallback to a complete HTML page in the output
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# def _detect_isolated_frame() -> bool:
|
|
110
|
+
# if not _is_IPython_display():
|
|
111
|
+
# return True # fallback to a complete HTML page in the output
|
|
112
|
+
#
|
|
113
|
+
# # Some notebooks behave like a single HTML page where the JS library is loaded once per notebook.
|
|
114
|
+
# if (_is_jupyter_classic() or
|
|
115
|
+
# _is_kaggle() or
|
|
116
|
+
# _is_positron_notebook()):
|
|
117
|
+
# return False # no iframes in the output cell
|
|
118
|
+
#
|
|
119
|
+
# # Most online notebook platforms are showing cell output in iframe and require
|
|
120
|
+
# # a complete HTML page in the output which includes both:
|
|
121
|
+
# # - the script loading JS library and
|
|
122
|
+
# # - the script that uses this JS lib to create a plot.
|
|
123
|
+
#
|
|
124
|
+
# if (_is_google_colab() or
|
|
125
|
+
# _is_azure_notebook() or
|
|
126
|
+
# _is_deepnote() or
|
|
127
|
+
# _is_databricks() or
|
|
128
|
+
# _is_nextjournal()
|
|
129
|
+
# ):
|
|
130
|
+
# return True # complete HTML page in the output
|
|
131
|
+
#
|
|
132
|
+
# # if os.getenv("PLOTLY_RENDERER") == "colab":
|
|
133
|
+
# # # good enough - something colab-like
|
|
134
|
+
# # return True # Colab -> iframe
|
|
135
|
+
#
|
|
136
|
+
# # try:
|
|
137
|
+
# # shell = get_ipython().__class__.__name__
|
|
138
|
+
# # if shell == 'ZMQInteractiveShell':
|
|
139
|
+
# # return False # Jupyter notebook or qtconsole -> load JS librarty once per notebook
|
|
140
|
+
# # elif shell == 'TerminalInteractiveShell':
|
|
141
|
+
# # return True # Terminal running IPython -> an isolated HTML page to show somehow
|
|
142
|
+
# # else:
|
|
143
|
+
# # return True # Other type (?)
|
|
144
|
+
# # except NameError:
|
|
145
|
+
# # return True # some other env (even standard Python interpreter) -> an isolated HTML page to show somehow
|
|
146
|
+
#
|
|
147
|
+
# # Fallback to a complete HTML page in the output
|
|
148
|
+
# return True
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _is_IPython_display() -> bool:
|
|
152
|
+
try:
|
|
153
|
+
from IPython.display import display_html
|
|
154
|
+
return True
|
|
155
|
+
except ImportError:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _is_jupyter_classic() -> bool:
|
|
160
|
+
# This also detects JupyterLab, which uses the same ZMQInteractiveShell
|
|
161
|
+
# and also qtconsole (allegedly)
|
|
162
|
+
try:
|
|
163
|
+
from IPython import get_ipython
|
|
164
|
+
except ImportError:
|
|
165
|
+
return False
|
|
166
|
+
shell = get_ipython()
|
|
167
|
+
try:
|
|
168
|
+
return shell is not None and shell.__class__.__name__ == "ZMQInteractiveShell"
|
|
169
|
+
except AttributeError:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _is_google_colab() -> bool:
|
|
174
|
+
try:
|
|
175
|
+
import google.colab
|
|
176
|
+
return True
|
|
177
|
+
except ImportError:
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _is_kaggle() -> bool:
|
|
182
|
+
return os.path.exists("/kaggle/input")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _is_azure_notebook() -> bool:
|
|
186
|
+
return "AZURE_NOTEBOOKS_HOST" in os.environ
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _is_deepnote() -> bool:
|
|
190
|
+
return "DEEPNOTE_PROJECT_ID" in os.environ
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _is_databricks() -> bool:
|
|
194
|
+
# As proposed: https://github.com/JetBrains/lets-plot/issues/602
|
|
195
|
+
return "databricks" in str(os.environ)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _is_nextjournal() -> bool:
|
|
199
|
+
return "NEXTJOURNAL" in str(os.environ)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _is_positron_console():
|
|
203
|
+
try:
|
|
204
|
+
from IPython import get_ipython
|
|
205
|
+
except ImportError:
|
|
206
|
+
return False
|
|
207
|
+
shell = get_ipython()
|
|
208
|
+
try:
|
|
209
|
+
return shell is not None and shell.session_mode == "console"
|
|
210
|
+
except AttributeError:
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _is_positron_notebook():
|
|
215
|
+
try:
|
|
216
|
+
from IPython import get_ipython
|
|
217
|
+
except ImportError:
|
|
218
|
+
return False
|
|
219
|
+
shell = get_ipython()
|
|
220
|
+
try:
|
|
221
|
+
return shell is not None and shell.session_mode == "notebook"
|
|
222
|
+
except AttributeError:
|
|
223
|
+
return False
|