1
|
|
"""
|
2
|
|
Defines the baseclasses that make a component render to a bokeh model
|
3
|
|
and become viewable including:
|
4
|
|
|
5
|
|
* Layoutable: Defines parameters concerned with layout and style
|
6
|
|
* ServableMixin: Mixin class that defines methods to serve object on server
|
7
|
|
* Renderable: Defines methods to render a component as a bokeh model
|
8
|
|
* Viewable: Defines methods to view the component in the
|
9
|
|
notebook, on the server or in static exports
|
10
|
|
"""
|
11
|
7
|
import datetime as dt
|
12
|
7
|
import logging
|
13
|
7
|
import sys
|
14
|
7
|
import traceback
|
15
|
7
|
import uuid
|
16
|
|
|
17
|
7
|
from functools import partial
|
18
|
|
|
19
|
7
|
import param
|
20
|
|
|
21
|
7
|
from bokeh.document.document import Document as _Document
|
22
|
7
|
from bokeh.io import curdoc as _curdoc
|
23
|
7
|
from pyviz_comms import JupyterCommManager
|
24
|
|
|
25
|
7
|
from .config import config, panel_extension
|
26
|
7
|
from .io.embed import embed_state
|
27
|
7
|
from .io.loading import start_loading_spinner, stop_loading_spinner
|
28
|
7
|
from .io.model import add_to_doc, patch_cds_msg
|
29
|
7
|
from .io.notebook import (
|
30
|
|
ipywidget, render_mimebundle, render_model, show_embed, show_server
|
31
|
|
)
|
32
|
7
|
from .io.save import save
|
33
|
7
|
from .io.state import state
|
34
|
7
|
from .io.server import init_doc, serve
|
35
|
7
|
from .util import escape, param_reprs
|
36
|
|
|
37
|
|
|
38
|
7
|
class Layoutable(param.Parameterized):
|
39
|
|
"""
|
40
|
|
Layoutable defines shared style and layout related parameters
|
41
|
|
for all Panel components with a visual representation.
|
42
|
|
"""
|
43
|
|
|
44
|
7
|
align = param.ObjectSelector(default='start',
|
45
|
|
objects=['start', 'end', 'center'], doc="""
|
46
|
|
Whether the object should be aligned with the start, end or
|
47
|
|
center of its container""")
|
48
|
|
|
49
|
7
|
aspect_ratio = param.Parameter(default=None, doc="""
|
50
|
|
Describes the proportional relationship between component's
|
51
|
|
width and height. This works if any of component's dimensions
|
52
|
|
are flexible in size. If set to a number, ``width / height =
|
53
|
|
aspect_ratio`` relationship will be maintained. Otherwise, if
|
54
|
|
set to ``"auto"``, component's preferred width and height will
|
55
|
|
be used to determine the aspect (if not set, no aspect will be
|
56
|
|
preserved).""")
|
57
|
|
|
58
|
7
|
background = param.Parameter(default=None, doc="""
|
59
|
|
Background color of the component.""")
|
60
|
|
|
61
|
7
|
css_classes = param.List(default=None, doc="""
|
62
|
|
CSS classes to apply to the layout.""")
|
63
|
|
|
64
|
7
|
width = param.Integer(default=None, bounds=(0, None), doc="""
|
65
|
|
The width of the component (in pixels). This can be either
|
66
|
|
fixed or preferred width, depending on width sizing policy.""")
|
67
|
|
|
68
|
7
|
height = param.Integer(default=None, bounds=(0, None), doc="""
|
69
|
|
The height of the component (in pixels). This can be either
|
70
|
|
fixed or preferred height, depending on height sizing policy.""")
|
71
|
|
|
72
|
7
|
min_width = param.Integer(default=None, bounds=(0, None), doc="""
|
73
|
|
Minimal width of the component (in pixels) if width is adjustable.""")
|
74
|
|
|
75
|
7
|
min_height = param.Integer(default=None, bounds=(0, None), doc="""
|
76
|
|
Minimal height of the component (in pixels) if height is adjustable.""")
|
77
|
|
|
78
|
7
|
max_width = param.Integer(default=None, bounds=(0, None), doc="""
|
79
|
|
Minimal width of the component (in pixels) if width is adjustable.""")
|
80
|
|
|
81
|
7
|
max_height = param.Integer(default=None, bounds=(0, None), doc="""
|
82
|
|
Minimal height of the component (in pixels) if height is adjustable.""")
|
83
|
|
|
84
|
7
|
margin = param.Parameter(default=5, doc="""
|
85
|
|
Allows to create additional space around the component. May
|
86
|
|
be specified as a two-tuple of the form (vertical, horizontal)
|
87
|
|
or a four-tuple (top, right, bottom, left).""")
|
88
|
|
|
89
|
7
|
width_policy = param.ObjectSelector(
|
90
|
|
default="auto", objects=['auto', 'fixed', 'fit', 'min', 'max'], doc="""
|
91
|
|
Describes how the component should maintain its width.
|
92
|
|
|
93
|
|
``"auto"``
|
94
|
|
Use component's preferred sizing policy.
|
95
|
|
|
96
|
|
``"fixed"``
|
97
|
|
Use exactly ``width`` pixels. Component will overflow if
|
98
|
|
it can't fit in the available horizontal space.
|
99
|
|
|
100
|
|
``"fit"``
|
101
|
|
Use component's preferred width (if set) and allow it to
|
102
|
|
fit into the available horizontal space within the minimum
|
103
|
|
and maximum width bounds (if set). Component's width
|
104
|
|
neither will be aggressively minimized nor maximized.
|
105
|
|
|
106
|
|
``"min"``
|
107
|
|
Use as little horizontal space as possible, not less than
|
108
|
|
the minimum width (if set). The starting point is the
|
109
|
|
preferred width (if set). The width of the component may
|
110
|
|
shrink or grow depending on the parent layout, aspect
|
111
|
|
management and other factors.
|
112
|
|
|
113
|
|
``"max"``
|
114
|
|
Use as much horizontal space as possible, not more than
|
115
|
|
the maximum width (if set). The starting point is the
|
116
|
|
preferred width (if set). The width of the component may
|
117
|
|
shrink or grow depending on the parent layout, aspect
|
118
|
|
management and other factors.
|
119
|
|
""")
|
120
|
|
|
121
|
7
|
height_policy = param.ObjectSelector(
|
122
|
|
default="auto", objects=['auto', 'fixed', 'fit', 'min', 'max'], doc="""
|
123
|
|
Describes how the component should maintain its height.
|
124
|
|
|
125
|
|
``"auto"``
|
126
|
|
Use component's preferred sizing policy.
|
127
|
|
|
128
|
|
``"fixed"``
|
129
|
|
Use exactly ``height`` pixels. Component will overflow if
|
130
|
|
it can't fit in the available vertical space.
|
131
|
|
|
132
|
|
``"fit"``
|
133
|
|
Use component's preferred height (if set) and allow to fit
|
134
|
|
into the available vertical space within the minimum and
|
135
|
|
maximum height bounds (if set). Component's height neither
|
136
|
|
will be aggressively minimized nor maximized.
|
137
|
|
|
138
|
|
``"min"``
|
139
|
|
Use as little vertical space as possible, not less than
|
140
|
|
the minimum height (if set). The starting point is the
|
141
|
|
preferred height (if set). The height of the component may
|
142
|
|
shrink or grow depending on the parent layout, aspect
|
143
|
|
management and other factors.
|
144
|
|
|
145
|
|
``"max"``
|
146
|
|
Use as much vertical space as possible, not more than the
|
147
|
|
maximum height (if set). The starting point is the
|
148
|
|
preferred height (if set). The height of the component may
|
149
|
|
shrink or grow depending on the parent layout, aspect
|
150
|
|
management and other factors.
|
151
|
|
""")
|
152
|
|
|
153
|
7
|
sizing_mode = param.ObjectSelector(default=None, objects=[
|
154
|
|
'fixed', 'stretch_width', 'stretch_height', 'stretch_both',
|
155
|
|
'scale_width', 'scale_height', 'scale_both', None], doc="""
|
156
|
|
|
157
|
|
How the component should size itself.
|
158
|
|
|
159
|
|
This is a high-level setting for maintaining width and height
|
160
|
|
of the component. To gain more fine grained control over
|
161
|
|
sizing, use ``width_policy``, ``height_policy`` and
|
162
|
|
``aspect_ratio`` instead (those take precedence over
|
163
|
|
``sizing_mode``).
|
164
|
|
|
165
|
|
``"fixed"``
|
166
|
|
Component is not responsive. It will retain its original
|
167
|
|
width and height regardless of any subsequent browser
|
168
|
|
window resize events.
|
169
|
|
|
170
|
|
``"stretch_width"``
|
171
|
|
Component will responsively resize to stretch to the
|
172
|
|
available width, without maintaining any aspect ratio. The
|
173
|
|
height of the component depends on the type of the
|
174
|
|
component and may be fixed or fit to component's contents.
|
175
|
|
|
176
|
|
``"stretch_height"``
|
177
|
|
Component will responsively resize to stretch to the
|
178
|
|
available height, without maintaining any aspect
|
179
|
|
ratio. The width of the component depends on the type of
|
180
|
|
the component and may be fixed or fit to component's
|
181
|
|
contents.
|
182
|
|
|
183
|
|
``"stretch_both"``
|
184
|
|
Component is completely responsive, independently in width
|
185
|
|
and height, and will occupy all the available horizontal
|
186
|
|
and vertical space, even if this changes the aspect ratio
|
187
|
|
of the component.
|
188
|
|
|
189
|
|
``"scale_width"``
|
190
|
|
Component will responsively resize to stretch to the
|
191
|
|
available width, while maintaining the original or
|
192
|
|
provided aspect ratio.
|
193
|
|
|
194
|
|
``"scale_height"``
|
195
|
|
Component will responsively resize to stretch to the
|
196
|
|
available height, while maintaining the original or
|
197
|
|
provided aspect ratio.
|
198
|
|
|
199
|
|
``"scale_both"``
|
200
|
|
Component will responsively resize to both the available
|
201
|
|
width and height, while maintaining the original or
|
202
|
|
provided aspect ratio.
|
203
|
|
""")
|
204
|
|
|
205
|
7
|
__abstract = True
|
206
|
|
|
207
|
7
|
def __init__(self, **params):
|
208
|
7
|
if (params.get('width', None) is not None and
|
209
|
|
params.get('height', None) is not None and
|
210
|
|
params.get('width_policy') is None and
|
211
|
|
params.get('height_policy') is None and
|
212
|
|
'sizing_mode' not in params):
|
213
|
7
|
params['sizing_mode'] = 'fixed'
|
214
|
7
|
elif (not (self.param.sizing_mode.constant or self.param.sizing_mode.readonly) and
|
215
|
|
type(self).sizing_mode is None):
|
216
|
7
|
params['sizing_mode'] = params.get('sizing_mode', config.sizing_mode)
|
217
|
7
|
super().__init__(**params)
|
218
|
|
|
219
|
|
|
220
|
7
|
class ServableMixin(object):
|
221
|
|
"""
|
222
|
|
Mixin to define methods shared by objects which can served.
|
223
|
|
"""
|
224
|
|
|
225
|
7
|
def _modify_doc(self, server_id, title, doc, location):
|
226
|
|
"""
|
227
|
|
Callback to handle FunctionHandler document creation.
|
228
|
|
"""
|
229
|
7
|
if server_id:
|
230
|
7
|
state._servers[server_id][2].append(doc)
|
231
|
7
|
return self.server_doc(doc, title, location)
|
232
|
|
|
233
|
7
|
def _add_location(self, doc, location, root=None):
|
234
|
7
|
from .io.location import Location
|
235
|
7
|
if isinstance(location, Location):
|
236
|
0
|
loc = location
|
237
|
7
|
elif doc in state._locations:
|
238
|
0
|
loc = state._locations[doc]
|
239
|
|
else:
|
240
|
7
|
loc = Location()
|
241
|
7
|
state._locations[doc] = loc
|
242
|
7
|
if root is None:
|
243
|
0
|
loc_model = loc._get_root(doc)
|
244
|
|
else:
|
245
|
7
|
loc_model = loc._get_model(doc, root)
|
246
|
7
|
loc_model.name = 'location'
|
247
|
7
|
doc.add_root(loc_model)
|
248
|
7
|
return loc
|
249
|
|
|
250
|
7
|
def _on_msg(self, ref, manager, msg):
|
251
|
|
"""
|
252
|
|
Handles Protocol messages arriving from the client comm.
|
253
|
|
"""
|
254
|
0
|
root, doc, comm = state._views[ref][1:]
|
255
|
0
|
patch_cds_msg(root, msg)
|
256
|
0
|
held = doc._hold
|
257
|
0
|
patch = manager.assemble(msg)
|
258
|
0
|
doc.hold()
|
259
|
0
|
patch.apply_to_document(doc, comm.id)
|
260
|
0
|
doc.unhold()
|
261
|
0
|
if held:
|
262
|
0
|
doc.hold(held)
|
263
|
|
|
264
|
7
|
def _on_error(self, ref, error):
|
265
|
7
|
if ref not in state._handles or config.console_output in [None, 'disable']:
|
266
|
7
|
return
|
267
|
7
|
handle, accumulator = state._handles[ref]
|
268
|
7
|
formatted = '\n<pre>'+escape(traceback.format_exc())+'</pre>\n'
|
269
|
7
|
if config.console_output == 'accumulate':
|
270
|
7
|
accumulator.append(formatted)
|
271
|
7
|
elif config.console_output == 'replace':
|
272
|
7
|
accumulator[:] = [formatted]
|
273
|
7
|
if accumulator:
|
274
|
7
|
handle.update({'text/html': '\n'.join(accumulator)}, raw=True)
|
275
|
|
|
276
|
7
|
def _on_stdout(self, ref, stdout):
|
277
|
7
|
if ref not in state._handles or config.console_output is [None, 'disable']:
|
278
|
0
|
return
|
279
|
7
|
handle, accumulator = state._handles[ref]
|
280
|
7
|
formatted = ["%s</br>" % o for o in stdout]
|
281
|
7
|
if config.console_output == 'accumulate':
|
282
|
7
|
accumulator.extend(formatted)
|
283
|
7
|
elif config.console_output == 'replace':
|
284
|
7
|
accumulator[:] = formatted
|
285
|
7
|
if accumulator:
|
286
|
7
|
handle.update({'text/html': '\n'.join(accumulator)}, raw=True)
|
287
|
|
|
288
|
|
#----------------------------------------------------------------
|
289
|
|
# Public API
|
290
|
|
#----------------------------------------------------------------
|
291
|
|
|
292
|
7
|
def servable(self, title=None, location=True):
|
293
|
|
"""
|
294
|
|
Serves the object if in a `panel serve` context and returns
|
295
|
|
the Panel object to allow it to display itself in a notebook
|
296
|
|
context.
|
297
|
|
Arguments
|
298
|
|
---------
|
299
|
|
title : str
|
300
|
|
A string title to give the Document (if served as an app)
|
301
|
|
location : boolean or panel.io.location.Location
|
302
|
|
Whether to create a Location component to observe and
|
303
|
|
set the URL location.
|
304
|
|
|
305
|
|
Returns
|
306
|
|
-------
|
307
|
|
The Panel object itself
|
308
|
|
"""
|
309
|
0
|
if _curdoc().session_context:
|
310
|
0
|
logger = logging.getLogger('bokeh')
|
311
|
0
|
for handler in logger.handlers:
|
312
|
0
|
if isinstance(handler, logging.StreamHandler):
|
313
|
0
|
handler.setLevel(logging.WARN)
|
314
|
0
|
self.server_doc(title=title, location=True)
|
315
|
0
|
return self
|
316
|
|
|
317
|
7
|
def show(self, title=None, port=0, address=None, websocket_origin=None,
|
318
|
|
threaded=False, verbose=True, open=True, location=True, **kwargs):
|
319
|
|
"""
|
320
|
|
Starts a Bokeh server and displays the Viewable in a new tab.
|
321
|
|
|
322
|
|
Arguments
|
323
|
|
---------
|
324
|
|
title : str
|
325
|
|
A string title to give the Document (if served as an app)
|
326
|
|
port: int (optional, default=0)
|
327
|
|
Allows specifying a specific port
|
328
|
|
address : str
|
329
|
|
The address the server should listen on for HTTP requests.
|
330
|
|
websocket_origin: str or list(str) (optional)
|
331
|
|
A list of hosts that can connect to the websocket.
|
332
|
|
This is typically required when embedding a server app in
|
333
|
|
an external web site.
|
334
|
|
If None, "localhost" is used.
|
335
|
|
threaded: boolean (optional, default=False)
|
336
|
|
Whether to launch the Server on a separate thread, allowing
|
337
|
|
interactive use.
|
338
|
|
verbose: boolean (optional, default=True)
|
339
|
|
Whether to print the address and port
|
340
|
|
open : boolean (optional, default=True)
|
341
|
|
Whether to open the server in a new browser tab
|
342
|
|
location : boolean or panel.io.location.Location
|
343
|
|
Whether to create a Location component to observe and
|
344
|
|
set the URL location.
|
345
|
|
|
346
|
|
Returns
|
347
|
|
-------
|
348
|
|
server: bokeh.server.Server or threading.Thread
|
349
|
|
Returns the Bokeh server instance or the thread the server
|
350
|
|
was launched on (if threaded=True)
|
351
|
|
"""
|
352
|
0
|
return serve(
|
353
|
|
self, port=port, address=address, websocket_origin=websocket_origin,
|
354
|
|
show=open, start=True, title=title, verbose=verbose,
|
355
|
|
location=location, threaded=threaded, **kwargs
|
356
|
|
)
|
357
|
|
|
358
|
|
|
359
|
7
|
class Renderable(param.Parameterized):
|
360
|
|
"""
|
361
|
|
Baseclass for objects which can be rendered to a Bokeh model.
|
362
|
|
|
363
|
|
It therefore declare APIs for initializing the models from
|
364
|
|
parameter values.
|
365
|
|
"""
|
366
|
|
|
367
|
7
|
__abstract = True
|
368
|
|
|
369
|
7
|
def __init__(self, **params):
|
370
|
7
|
super().__init__(**params)
|
371
|
7
|
self._callbacks = []
|
372
|
7
|
self._documents = {}
|
373
|
7
|
self._models = {}
|
374
|
7
|
self._comms = {}
|
375
|
7
|
self._kernels = {}
|
376
|
7
|
self._found_links = set()
|
377
|
|
|
378
|
7
|
def _get_model(self, doc, root=None, parent=None, comm=None):
|
379
|
|
"""
|
380
|
|
Converts the objects being wrapped by the viewable into a
|
381
|
|
bokeh model that can be composed in a bokeh layout.
|
382
|
|
|
383
|
|
Arguments
|
384
|
|
----------
|
385
|
|
doc: bokeh.Document
|
386
|
|
Bokeh document the bokeh model will be attached to.
|
387
|
|
root: bokeh.Model
|
388
|
|
The root layout the viewable will become part of.
|
389
|
|
parent: bokeh.Model
|
390
|
|
The parent layout the viewable will become part of.
|
391
|
|
comm: pyviz_comms.Comm
|
392
|
|
Optional pyviz_comms when working in notebook
|
393
|
|
|
394
|
|
Returns
|
395
|
|
-------
|
396
|
|
model: bokeh.Model
|
397
|
|
"""
|
398
|
0
|
raise NotImplementedError
|
399
|
|
|
400
|
7
|
def _cleanup(self, root):
|
401
|
|
"""
|
402
|
|
Clean up method which is called when a Viewable is destroyed.
|
403
|
|
|
404
|
|
Arguments
|
405
|
|
---------
|
406
|
|
root: bokeh.model.Model
|
407
|
|
Bokeh model for the view being cleaned up
|
408
|
|
"""
|
409
|
7
|
ref = root.ref['id']
|
410
|
7
|
if ref in state._handles:
|
411
|
7
|
del state._handles[ref]
|
412
|
|
|
413
|
7
|
def _preprocess(self, root):
|
414
|
|
"""
|
415
|
|
Applies preprocessing hooks to the model.
|
416
|
|
"""
|
417
|
7
|
hooks = self._preprocessing_hooks+self._hooks
|
418
|
7
|
for hook in hooks:
|
419
|
7
|
hook(self, root)
|
420
|
|
|
421
|
7
|
def _render_model(self, doc=None, comm=None):
|
422
|
0
|
if doc is None:
|
423
|
0
|
doc = _Document()
|
424
|
0
|
if comm is None:
|
425
|
0
|
comm = state._comm_manager.get_server_comm()
|
426
|
0
|
model = self.get_root(doc, comm)
|
427
|
|
|
428
|
0
|
if config.embed:
|
429
|
0
|
embed_state(self, model, doc,
|
430
|
|
json=config.embed_json,
|
431
|
|
json_prefix=config.embed_json_prefix,
|
432
|
|
save_path=config.embed_save_path,
|
433
|
|
load_path=config.embed_load_path,
|
434
|
|
progress=False)
|
435
|
|
else:
|
436
|
0
|
add_to_doc(model, doc)
|
437
|
0
|
return model
|
438
|
|
|
439
|
7
|
def _init_params(self):
|
440
|
0
|
return {k: v for k, v in self.param.get_param_values() if v is not None}
|
441
|
|
|
442
|
7
|
def _server_destroy(self, session_context):
|
443
|
|
"""
|
444
|
|
Server lifecycle hook triggered when session is destroyed.
|
445
|
|
"""
|
446
|
7
|
session_id = session_context.id
|
447
|
7
|
sessions = state.session_info['sessions']
|
448
|
7
|
if session_id in sessions and sessions[session_id]['ended'] is None:
|
449
|
7
|
session = sessions[session_id]
|
450
|
7
|
if session['rendered'] is not None:
|
451
|
7
|
state.session_info['live'] -= 1
|
452
|
7
|
session['ended'] = dt.datetime.now().timestamp()
|
453
|
7
|
doc = session_context._document
|
454
|
7
|
root = self._documents[doc]
|
455
|
7
|
ref = root.ref['id']
|
456
|
7
|
self._cleanup(root)
|
457
|
7
|
del self._documents[doc]
|
458
|
7
|
if ref in state._views:
|
459
|
7
|
del state._views[ref]
|
460
|
7
|
if doc in state._locations:
|
461
|
7
|
loc = state._locations[doc]
|
462
|
7
|
loc._cleanup(root)
|
463
|
7
|
del state._locations[doc]
|
464
|
|
|
465
|
7
|
def get_root(self, doc=None, comm=None, preprocess=True):
|
466
|
|
"""
|
467
|
|
Returns the root model and applies pre-processing hooks
|
468
|
|
|
469
|
|
Arguments
|
470
|
|
---------
|
471
|
|
doc: bokeh.Document
|
472
|
|
Bokeh document the bokeh model will be attached to.
|
473
|
|
comm: pyviz_comms.Comm
|
474
|
|
Optional pyviz_comms when working in notebook
|
475
|
|
preprocess: boolean (default=True)
|
476
|
|
Whether to run preprocessing hooks
|
477
|
|
|
478
|
|
Returns
|
479
|
|
-------
|
480
|
|
Returns the bokeh model corresponding to this panel object
|
481
|
|
"""
|
482
|
7
|
doc = init_doc(doc)
|
483
|
7
|
root = self._get_model(doc, comm=comm)
|
484
|
7
|
if preprocess:
|
485
|
7
|
self._preprocess(root)
|
486
|
7
|
ref = root.ref['id']
|
487
|
7
|
state._views[ref] = (self, root, doc, comm)
|
488
|
7
|
return root
|
489
|
|
|
490
|
|
|
491
|
7
|
class Viewable(Renderable, Layoutable, ServableMixin):
|
492
|
|
"""
|
493
|
|
Viewable is the baseclass all visual components in the panel
|
494
|
|
library are built on. It defines the interface for declaring any
|
495
|
|
object that displays itself by transforming the object(s) being
|
496
|
|
wrapped into models that can be served using bokeh's layout
|
497
|
|
engine. The class also defines various methods that allow Viewable
|
498
|
|
objects to be displayed in the notebook and on bokeh server.
|
499
|
|
"""
|
500
|
|
|
501
|
7
|
loading = param.Boolean(doc="""
|
502
|
|
Whether or not the Viewable is loading. If True a loading spinner
|
503
|
|
is shown on top of the Viewable.""")
|
504
|
|
|
505
|
7
|
_preprocessing_hooks = []
|
506
|
|
|
507
|
7
|
def __init__(self, **params):
|
508
|
7
|
hooks = params.pop('hooks', [])
|
509
|
7
|
super().__init__(**params)
|
510
|
7
|
self._hooks = hooks
|
511
|
7
|
self._update_loading()
|
512
|
7
|
watcher = self.param.watch(self._update_loading, 'loading')
|
513
|
7
|
self._callbacks.append(watcher)
|
514
|
|
|
515
|
7
|
def _update_loading(self, *_):
|
516
|
7
|
if self.loading:
|
517
|
0
|
start_loading_spinner(self)
|
518
|
|
else:
|
519
|
7
|
stop_loading_spinner(self)
|
520
|
|
|
521
|
7
|
def __repr__(self, depth=0):
|
522
|
7
|
return '{cls}({params})'.format(cls=type(self).__name__,
|
523
|
|
params=', '.join(param_reprs(self)))
|
524
|
|
|
525
|
7
|
def __str__(self):
|
526
|
7
|
return self.__repr__()
|
527
|
|
|
528
|
7
|
def _repr_mimebundle_(self, include=None, exclude=None):
|
529
|
7
|
loaded = panel_extension._loaded
|
530
|
7
|
if not loaded and 'holoviews' in sys.modules:
|
531
|
7
|
import holoviews as hv
|
532
|
7
|
loaded = hv.extension._loaded
|
533
|
|
|
534
|
|
|
535
|
7
|
if config.comms in ('vscode', 'ipywidgets'):
|
536
|
7
|
widget = ipywidget(self)
|
537
|
7
|
if hasattr(widget, '_repr_mimebundle_'):
|
538
|
0
|
return widget._repr_mimebundle_(include, exclude)
|
539
|
7
|
plaintext = repr(widget)
|
540
|
7
|
if len(plaintext) > 110:
|
541
|
7
|
plaintext = plaintext[:110] + '…'
|
542
|
7
|
data = {
|
543
|
|
'text/plain': plaintext,
|
544
|
|
}
|
545
|
7
|
if widget._view_name is not None:
|
546
|
7
|
data['application/vnd.jupyter.widget-view+json'] = {
|
547
|
|
'version_major': 2,
|
548
|
|
'version_minor': 0,
|
549
|
|
'model_id': widget._model_id
|
550
|
|
}
|
551
|
7
|
if config.comms == 'vscode':
|
552
|
0
|
from IPython.display import display
|
553
|
0
|
display(data, raw=True)
|
554
|
0
|
return {'text/html': '<div style="display: none"></div>'}, {}
|
555
|
7
|
return data, {}
|
556
|
|
|
557
|
0
|
if not loaded:
|
558
|
0
|
self.param.warning('Displaying Panel objects in the notebook '
|
559
|
|
'requires the panel extension to be loaded. '
|
560
|
|
'Ensure you run pn.extension() before '
|
561
|
|
'displaying objects in the notebook.')
|
562
|
0
|
return None
|
563
|
|
|
564
|
0
|
if config.comms == 'colab':
|
565
|
0
|
from .io.notebook import load_notebook
|
566
|
0
|
load_notebook(config.inline)
|
567
|
|
|
568
|
0
|
try:
|
569
|
0
|
from IPython import get_ipython
|
570
|
0
|
assert get_ipython().kernel is not None
|
571
|
0
|
state._comm_manager = JupyterCommManager
|
572
|
0
|
except Exception:
|
573
|
0
|
pass
|
574
|
|
|
575
|
0
|
if not state._views:
|
576
|
|
# Initialize the global Location
|
577
|
0
|
from .io.location import Location
|
578
|
0
|
state._location = location = Location()
|
579
|
|
else:
|
580
|
0
|
location = None
|
581
|
|
|
582
|
0
|
from IPython.display import display
|
583
|
0
|
from .models.comm_manager import CommManager
|
584
|
|
|
585
|
0
|
doc = _Document()
|
586
|
0
|
comm = state._comm_manager.get_server_comm()
|
587
|
0
|
model = self._render_model(doc, comm)
|
588
|
0
|
ref = model.ref['id']
|
589
|
0
|
manager = CommManager(comm_id=comm.id, plot_id=ref)
|
590
|
0
|
client_comm = state._comm_manager.get_client_comm(
|
591
|
|
on_msg=partial(self._on_msg, ref, manager),
|
592
|
|
on_error=partial(self._on_error, ref),
|
593
|
|
on_stdout=partial(self._on_stdout, ref)
|
594
|
|
)
|
595
|
0
|
self._comms[ref] = (comm, client_comm)
|
596
|
0
|
manager.client_comm_id = client_comm.id
|
597
|
|
|
598
|
0
|
if config.console_output != 'disable':
|
599
|
0
|
handle = display(display_id=uuid.uuid4().hex)
|
600
|
0
|
state._handles[ref] = (handle, [])
|
601
|
|
|
602
|
0
|
if config.embed:
|
603
|
0
|
return render_model(model)
|
604
|
0
|
return render_mimebundle(model, doc, comm, manager, location)
|
605
|
|
|
606
|
|
#----------------------------------------------------------------
|
607
|
|
# Public API
|
608
|
|
#----------------------------------------------------------------
|
609
|
|
|
610
|
7
|
def clone(self, **params):
|
611
|
|
"""
|
612
|
|
Makes a copy of the object sharing the same parameters.
|
613
|
|
|
614
|
|
Arguments
|
615
|
|
---------
|
616
|
|
params: Keyword arguments override the parameters on the clone.
|
617
|
|
|
618
|
|
Returns
|
619
|
|
-------
|
620
|
|
Cloned Viewable object
|
621
|
|
"""
|
622
|
7
|
inherited = {p: v for p, v in self.param.get_param_values()
|
623
|
|
if not self.param[p].readonly}
|
624
|
7
|
return type(self)(**dict(inherited, **params))
|
625
|
|
|
626
|
7
|
def pprint(self):
|
627
|
|
"""
|
628
|
|
Prints a compositional repr of the class.
|
629
|
|
"""
|
630
|
0
|
print(self)
|
631
|
|
|
632
|
7
|
def select(self, selector=None):
|
633
|
|
"""
|
634
|
|
Iterates over the Viewable and any potential children in the
|
635
|
|
applying the Selector.
|
636
|
|
|
637
|
|
Arguments
|
638
|
|
---------
|
639
|
|
selector: type or callable or None
|
640
|
|
The selector allows selecting a subset of Viewables by
|
641
|
|
declaring a type or callable function to filter by.
|
642
|
|
|
643
|
|
Returns
|
644
|
|
-------
|
645
|
|
viewables: list(Viewable)
|
646
|
|
"""
|
647
|
7
|
if (selector is None or
|
648
|
|
(isinstance(selector, type) and isinstance(self, selector)) or
|
649
|
|
(callable(selector) and not isinstance(selector, type) and selector(self))):
|
650
|
7
|
return [self]
|
651
|
|
else:
|
652
|
7
|
return []
|
653
|
|
|
654
|
7
|
def app(self, notebook_url="localhost:8888", port=0):
|
655
|
|
"""
|
656
|
|
Displays a bokeh server app inline in the notebook.
|
657
|
|
|
658
|
|
Arguments
|
659
|
|
---------
|
660
|
|
notebook_url: str
|
661
|
|
URL to the notebook server
|
662
|
|
port: int (optional, default=0)
|
663
|
|
Allows specifying a specific port
|
664
|
|
"""
|
665
|
0
|
return show_server(self, notebook_url, port)
|
666
|
|
|
667
|
7
|
def embed(self, max_states=1000, max_opts=3, json=False, json_prefix='',
|
668
|
|
save_path='./', load_path=None, progress=False, states={}):
|
669
|
|
"""
|
670
|
|
Renders a static version of a panel in a notebook by evaluating
|
671
|
|
the set of states defined by the widgets in the model. Note
|
672
|
|
this will only work well for simple apps with a relatively
|
673
|
|
small state space.
|
674
|
|
|
675
|
|
Arguments
|
676
|
|
---------
|
677
|
|
max_states: int
|
678
|
|
The maximum number of states to embed
|
679
|
|
max_opts: int
|
680
|
|
The maximum number of states for a single widget
|
681
|
|
json: boolean (default=True)
|
682
|
|
Whether to export the data to json files
|
683
|
|
json_prefix: str (default='')
|
684
|
|
Prefix for JSON filename
|
685
|
|
save_path: str (default='./')
|
686
|
|
The path to save json files to
|
687
|
|
load_path: str (default=None)
|
688
|
|
The path or URL the json files will be loaded from.
|
689
|
|
progress: boolean (default=False)
|
690
|
|
Whether to report progress
|
691
|
|
states: dict (default={})
|
692
|
|
A dictionary specifying the widget values to embed for each widget
|
693
|
|
"""
|
694
|
0
|
show_embed(
|
695
|
|
self, max_states, max_opts, json, json_prefix, save_path,
|
696
|
|
load_path, progress, states
|
697
|
|
)
|
698
|
|
|
699
|
7
|
def save(self, filename, title=None, resources=None, template=None,
|
700
|
|
template_variables=None, embed=False, max_states=1000,
|
701
|
|
max_opts=3, embed_json=False, json_prefix='', save_path='./',
|
702
|
|
load_path=None, progress=True, embed_states={}):
|
703
|
|
"""
|
704
|
|
Saves Panel objects to file.
|
705
|
|
|
706
|
|
Arguments
|
707
|
|
---------
|
708
|
|
filename: string or file-like object
|
709
|
|
Filename to save the plot to
|
710
|
|
title: string
|
711
|
|
Optional title for the plot
|
712
|
|
resources: bokeh resources
|
713
|
|
One of the valid bokeh.resources (e.g. CDN or INLINE)
|
714
|
|
template:
|
715
|
|
passed to underlying io.save
|
716
|
|
template_variables:
|
717
|
|
passed to underlying io.save
|
718
|
|
embed: bool
|
719
|
|
Whether the state space should be embedded in the saved file.
|
720
|
|
max_states: int
|
721
|
|
The maximum number of states to embed
|
722
|
|
max_opts: int
|
723
|
|
The maximum number of states for a single widget
|
724
|
|
embed_json: boolean (default=True)
|
725
|
|
Whether to export the data to json files
|
726
|
|
json_prefix: str (default='')
|
727
|
|
Prefix for the auto-generated json directory
|
728
|
|
save_path: str (default='./')
|
729
|
|
The path to save json files to
|
730
|
|
load_path: str (default=None)
|
731
|
|
The path or URL the json files will be loaded from.
|
732
|
|
progress: boolean (default=True)
|
733
|
|
Whether to report progress
|
734
|
|
embed_states: dict (default={})
|
735
|
|
A dictionary specifying the widget values to embed for each widget
|
736
|
|
"""
|
737
|
7
|
return save(self, filename, title, resources, template,
|
738
|
|
template_variables, embed, max_states, max_opts,
|
739
|
|
embed_json, json_prefix, save_path, load_path,
|
740
|
|
progress, embed_states)
|
741
|
|
|
742
|
7
|
def server_doc(self, doc=None, title=None, location=True):
|
743
|
|
"""
|
744
|
|
Returns a serveable bokeh Document with the panel attached
|
745
|
|
|
746
|
|
Arguments
|
747
|
|
---------
|
748
|
|
doc : bokeh.Document (optional)
|
749
|
|
The bokeh Document to attach the panel to as a root,
|
750
|
|
defaults to bokeh.io.curdoc()
|
751
|
|
title : str
|
752
|
|
A string title to give the Document
|
753
|
|
location : boolean or panel.io.location.Location
|
754
|
|
Whether to create a Location component to observe and
|
755
|
|
set the URL location.
|
756
|
|
|
757
|
|
Returns
|
758
|
|
-------
|
759
|
|
doc : bokeh.Document
|
760
|
|
The bokeh document the panel was attached to
|
761
|
|
"""
|
762
|
7
|
doc = init_doc(doc)
|
763
|
7
|
title = title or 'Panel Application'
|
764
|
7
|
doc.title = title
|
765
|
7
|
model = self.get_root(doc)
|
766
|
7
|
if hasattr(doc, 'on_session_destroyed'):
|
767
|
7
|
doc.on_session_destroyed(self._server_destroy)
|
768
|
7
|
self._documents[doc] = model
|
769
|
7
|
add_to_doc(model, doc)
|
770
|
7
|
if location: self._add_location(doc, location, model)
|
771
|
7
|
return doc
|