cubevis 0.5.29__py3-none-any.whl → 1.0.3__py3-none-any.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.
- cubevis/__init__.py +13 -24
- cubevis/__js__/bokeh-3.6/cubevisjs.min.js +64 -0
- cubevis/__js__/{cubevisjs.min.js → bokeh-3.7/cubevisjs.min.js} +3 -3
- cubevis/__js__/bokeh-3.8/cubevisjs.min.js +64 -0
- cubevis/__js__/casalib.min.js +1 -1
- cubevis/__version__.py +1 -1
- cubevis/bokeh/__init__.py +16 -0
- cubevis/bokeh/annotations/_ev_poly_annotation.py +2 -1
- cubevis/bokeh/format/_wcs_ticks.py +6 -2
- cubevis/bokeh/models/__init__.py +1 -0
- cubevis/bokeh/models/_showable.py +352 -0
- cubevis/bokeh/models/_tip.py +2 -1
- cubevis/bokeh/models/_tip_button.py +2 -3
- cubevis/bokeh/sources/_data_pipe.py +6 -2
- cubevis/bokeh/sources/_image_data_source.py +6 -2
- cubevis/bokeh/sources/_image_pipe.py +4 -1
- cubevis/bokeh/sources/_spectra_data_source.py +6 -3
- cubevis/bokeh/sources/_updatable_data_source.py +6 -2
- cubevis/bokeh/state/__init__.py +4 -3
- cubevis/bokeh/state/_current.py +34 -0
- cubevis/bokeh/state/_initialize.py +282 -116
- cubevis/bokeh/state/_javascript.py +95 -21
- cubevis/bokeh/tools/_cbreset_tool.py +2 -1
- cubevis/bokeh/tools/_drag_tool.py +2 -1
- cubevis/bokeh/utils/__init__.py +0 -1
- cubevis/exe/_setting.py +1 -0
- cubevis/private/apps/__init__.py +4 -2
- cubevis/private/apps/_interactiveclean.mustache +6 -2
- cubevis/private/apps/_interactiveclean.py +6 -2
- cubevis/private/apps/_interactivecleanjpy.mustache +112 -0
- cubevis/private/apps/_interactivecleanjpy.py +1874 -0
- cubevis/private/casatasks/__init__.py +1 -0
- cubevis/private/casatasks/icleanjpy.py +1831 -0
- cubevis/toolbox/_app_context.py +5 -9
- cubevis/toolbox/_cube.py +6 -2
- cubevis/toolbox/_interactive_clean_ui.mustache +20 -31
- cubevis/toolbox/_interactive_clean_ui.py +20 -31
- cubevis/utils/__init__.py +120 -18
- cubevis/utils/_git.py +36 -0
- cubevis/utils/_jupyter.py +12 -0
- {cubevis-0.5.29.dist-info → cubevis-1.0.3.dist-info}/METADATA +3 -3
- {cubevis-0.5.29.dist-info → cubevis-1.0.3.dist-info}/RECORD +44 -39
- cubevis/__js__/bokeh-3.6.1.min.js +0 -728
- cubevis/__js__/bokeh-tables-3.6.1.min.js +0 -119
- cubevis/__js__/bokeh-widgets-3.6.1.min.js +0 -141
- {cubevis-0.5.29.dist-info → cubevis-1.0.3.dist-info}/WHEEL +0 -0
- {cubevis-0.5.29.dist-info → cubevis-1.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from bokeh.models.layouts import LayoutDOM
|
|
3
|
+
from bokeh.models.ui import UIElement
|
|
4
|
+
from bokeh.core.properties import Instance
|
|
5
|
+
from bokeh.io import curdoc
|
|
6
|
+
from .. import BokehInit
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class Showable(LayoutDOM,BokehInit):
|
|
11
|
+
"""Wrap a UIElement to make any Bokeh UI component showable with show()
|
|
12
|
+
|
|
13
|
+
This class works by acting as a simple container that delegates to its UI element.
|
|
14
|
+
For Jupyter notebook display, use show(showable) - automatic display via _repr_mimebundle_
|
|
15
|
+
is not reliably supported by Bokeh's architecture.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def document(self):
|
|
20
|
+
"""Get the document this model is attached to."""
|
|
21
|
+
return getattr(self, '_document', None)
|
|
22
|
+
|
|
23
|
+
@document.setter
|
|
24
|
+
def document(self, doc):
|
|
25
|
+
"""
|
|
26
|
+
Intercept when Bokeh tries to attach us to a document.
|
|
27
|
+
This is called by bokeh.plotting.show() when it adds us to a document.
|
|
28
|
+
"""
|
|
29
|
+
from bokeh.io.state import curstate
|
|
30
|
+
import traceback
|
|
31
|
+
|
|
32
|
+
# Allow None (detaching from document)
|
|
33
|
+
if doc is None:
|
|
34
|
+
self._document = None
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
state = curstate()
|
|
38
|
+
|
|
39
|
+
# Check calling context
|
|
40
|
+
stack = traceback.extract_stack()
|
|
41
|
+
|
|
42
|
+
# Detect if called from bokeh.plotting.show or bokeh.io.show
|
|
43
|
+
called_from_bokeh_show = any(
|
|
44
|
+
('bokeh/io/' in frame.filename or 'bokeh\\io\\' in frame.filename or
|
|
45
|
+
'bokeh/plotting/' in frame.filename or 'bokeh\\plotting\\' in frame.filename)
|
|
46
|
+
for frame in stack[:-2] # Exclude the last 2 frames (this setter and __setattr__)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Check if called from our own methods
|
|
50
|
+
called_from_our_methods = any(
|
|
51
|
+
'Showable' in str(frame.line) or frame.filename.endswith('showable.py')
|
|
52
|
+
for frame in stack[-5:-2] # Check recent frames
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if state.file and called_from_bokeh_show and not called_from_our_methods:
|
|
56
|
+
raise RuntimeError(
|
|
57
|
+
f"\n{'='*70}\n"
|
|
58
|
+
f"❌ Cannot use bokeh.plotting.show() with {self.__class__.__name__}\n\n"
|
|
59
|
+
f"Please use one of these methods instead:\n"
|
|
60
|
+
f" • my_showable.show() # Custom show method\n"
|
|
61
|
+
f" • my_showable # Automatic display (evaluate in cell)\n\n"
|
|
62
|
+
f"Reason: bokeh.plotting.show() doesn't properly handle the custom\n"
|
|
63
|
+
f"sizing and backend requirements of Showable objects.\n"
|
|
64
|
+
f"{'='*70}\n"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Validate environment
|
|
68
|
+
if not state.notebook:
|
|
69
|
+
raise RuntimeError(
|
|
70
|
+
f"{self.__class__.__name__} can only be displayed in Jupyter notebooks.\n"
|
|
71
|
+
f"Please run:\n"
|
|
72
|
+
f" from bokeh.io import output_notebook\n"
|
|
73
|
+
f" output_notebook()"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Apply notebook sizing before attaching to document
|
|
77
|
+
if self._notebook_sizing == 'fixed':
|
|
78
|
+
self.sizing_mode = None
|
|
79
|
+
self.width = self._notebook_width
|
|
80
|
+
self.height = self._notebook_height
|
|
81
|
+
|
|
82
|
+
# Now set the document
|
|
83
|
+
self._document = doc
|
|
84
|
+
|
|
85
|
+
# Start backend if needed
|
|
86
|
+
if not hasattr(self, '_backend_started') or not self._backend_started:
|
|
87
|
+
self._start_backend()
|
|
88
|
+
self._backend_started = True
|
|
89
|
+
|
|
90
|
+
def __init__( self, ui_element=None, backend_func=None,
|
|
91
|
+
result_retrieval=None,
|
|
92
|
+
notebook_width=1200, notebook_height=800,
|
|
93
|
+
notebook_sizing='fixed', **kwargs):
|
|
94
|
+
logger.debug(f"\tShowable::__init__(ui_element={type(ui_element).__name__ if ui_element else None}, {kwargs}): {id(self)}")
|
|
95
|
+
|
|
96
|
+
# Set default sizing if not provided
|
|
97
|
+
sizing_params = {'sizing_mode', 'width', 'height'}
|
|
98
|
+
provided_sizing_params = set(kwargs.keys()) & sizing_params
|
|
99
|
+
if not provided_sizing_params:
|
|
100
|
+
kwargs['sizing_mode'] = 'stretch_both'
|
|
101
|
+
|
|
102
|
+
# CRITICAL FIX: Don't call _ensure_in_document during __init__
|
|
103
|
+
# Let Bokeh handle document management through the normal flow
|
|
104
|
+
super().__init__(**kwargs)
|
|
105
|
+
|
|
106
|
+
# Set the UI element
|
|
107
|
+
if ui_element is not None:
|
|
108
|
+
self.ui = ui_element
|
|
109
|
+
|
|
110
|
+
# Set the function to be called upon display
|
|
111
|
+
if backend_func is not None:
|
|
112
|
+
self._backend_startup_callback = backend_func
|
|
113
|
+
# function to be called to fetch the Showable GUI
|
|
114
|
+
# result (if one is/will be available)...
|
|
115
|
+
self._result_retrieval = result_retrieval
|
|
116
|
+
|
|
117
|
+
self._notebook_width = notebook_width
|
|
118
|
+
self._notebook_height = notebook_height
|
|
119
|
+
self._notebook_sizing = notebook_sizing # 'fixed' or 'stretch'
|
|
120
|
+
self._notebook_rendering = None
|
|
121
|
+
|
|
122
|
+
ui = Instance(UIElement, help="""
|
|
123
|
+
A UI element, which can be plots, layouts, widgets, or any other UIElement.
|
|
124
|
+
""")
|
|
125
|
+
|
|
126
|
+
# FIXED: Remove the children property override
|
|
127
|
+
# Let LayoutDOM handle its own children management
|
|
128
|
+
# The TypeScript side will handle the UI element rendering
|
|
129
|
+
|
|
130
|
+
def _sphinx_height_hint(self):
|
|
131
|
+
"""Delegate height hint to the wrapped UI element"""
|
|
132
|
+
logger.debug(f"\tShowable::_sphinx_height_hint(): {id(self)}")
|
|
133
|
+
if self.ui and hasattr(self.ui, '_sphinx_height_hint'):
|
|
134
|
+
return self.ui._sphinx_height_hint()
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
def _ensure_in_document(self):
|
|
138
|
+
"""Ensure this Showable is in the current document"""
|
|
139
|
+
from bokeh.io import curdoc
|
|
140
|
+
current_doc = curdoc()
|
|
141
|
+
|
|
142
|
+
# FIXED: More careful document management
|
|
143
|
+
# Only add to document if we're not already in the right one
|
|
144
|
+
if self.document is None:
|
|
145
|
+
current_doc.add_root(self)
|
|
146
|
+
logger.debug(f"\tShowable::_ensure_in_document(): Added {id(self)} to document {id(current_doc)}")
|
|
147
|
+
elif self.document is not current_doc:
|
|
148
|
+
# Remove from old document first
|
|
149
|
+
if self in self.document.roots:
|
|
150
|
+
self.document.remove_root(self)
|
|
151
|
+
current_doc.add_root(self)
|
|
152
|
+
logger.debug(f"\tShowable::_ensure_in_document(): Moved {id(self)} to document {id(current_doc)}")
|
|
153
|
+
|
|
154
|
+
# HOOK: Backend startup when added to document
|
|
155
|
+
# This catches both direct show() calls and Bokeh's show() function
|
|
156
|
+
if not hasattr(self, '_backend_started'):
|
|
157
|
+
self._start_backend( )
|
|
158
|
+
self._backend_started = True
|
|
159
|
+
|
|
160
|
+
def get_future(self):
|
|
161
|
+
if self._result_retrieval is None:
|
|
162
|
+
raise RuntimeError( f"{self.name if self.name else 'this showable'} does not return a result" )
|
|
163
|
+
else:
|
|
164
|
+
return self._result_retrieval( )
|
|
165
|
+
|
|
166
|
+
def get_result(self):
|
|
167
|
+
if self._result_retrieval is None:
|
|
168
|
+
raise RuntimeError( f"{self.name if self.name else 'this showable'} does not return a result" )
|
|
169
|
+
else:
|
|
170
|
+
return self._result_retrieval( ).result( )
|
|
171
|
+
|
|
172
|
+
def _start_backend(self):
|
|
173
|
+
"""Hook to start backend services when showing"""
|
|
174
|
+
# Override this in subclasses or set a callback
|
|
175
|
+
if hasattr(self, '_backend_startup_count'):
|
|
176
|
+
### backend has already been started
|
|
177
|
+
### must figure out what is the proper way to handle this case
|
|
178
|
+
logger.debug(f"\tShowable::_start_backend(): backend already started for {id(self)} [{self._backend_startup_count}]")
|
|
179
|
+
self._backend_startup_count += 1
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
if hasattr(self, '_backend_startup_callback'):
|
|
183
|
+
try:
|
|
184
|
+
self._backend_startup_callback()
|
|
185
|
+
logger.debug(f"\tShowable::_start_backend(): Executed startup callback for {id(self)}")
|
|
186
|
+
self._backend_startup_count = 1
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.error(f"\tShowable::_start_backend(): Error in startup callback: {e}")
|
|
189
|
+
|
|
190
|
+
# Example: Start asyncio backend
|
|
191
|
+
# if hasattr(self, '_backend_manager'):
|
|
192
|
+
# self._backend_manager.start()
|
|
193
|
+
|
|
194
|
+
logger.debug(f"\tShowable::_start_backend(): Backend startup hook called for {id(self)}")
|
|
195
|
+
|
|
196
|
+
def set_backend_startup_callback(self, callback):
|
|
197
|
+
"""Set a callback to be called when show() is invoked"""
|
|
198
|
+
if not callable(callback):
|
|
199
|
+
raise ValueError("Backend startup callback must be callable")
|
|
200
|
+
self._backend_startup_callback = callback
|
|
201
|
+
logger.debug(f"\tShowable::set_backend_startup_callback(): Set callback for {id(self)}")
|
|
202
|
+
|
|
203
|
+
def _stop_backend(self):
|
|
204
|
+
"""Hook to stop backend services - override in subclasses"""
|
|
205
|
+
if hasattr(self, '_backend_cleanup_callback'):
|
|
206
|
+
try:
|
|
207
|
+
self._backend_cleanup_callback()
|
|
208
|
+
logger.debug(f"\tShowable::_stop_backend(): Executed cleanup callback for {id(self)}")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"\tShowable::_stop_backend(): Error in cleanup callback: {e}")
|
|
211
|
+
|
|
212
|
+
logger.debug(f"\tShowable::_stop_backend(): Backend cleanup hook called for {id(self)}")
|
|
213
|
+
|
|
214
|
+
def set_backend_cleanup_callback(self, callback):
|
|
215
|
+
"""Set a callback to be called when cleaning up backend"""
|
|
216
|
+
if not callable(callback):
|
|
217
|
+
raise ValueError("Backend cleanup callback must be callable")
|
|
218
|
+
self._backend_cleanup_callback = callback
|
|
219
|
+
logger.debug(f"\tShowable::set_backend_cleanup_callback(): Set callback for {id(self)}")
|
|
220
|
+
|
|
221
|
+
def __del__(self):
|
|
222
|
+
"""Cleanup when Showable is destroyed"""
|
|
223
|
+
if hasattr(self, '_backend_startup_callback') and self._backend_startup_callback:
|
|
224
|
+
self._stop_backend()
|
|
225
|
+
|
|
226
|
+
def _get_notebook_html(self, start_backend=True):
|
|
227
|
+
"""
|
|
228
|
+
Common logic for generating HTML in notebook environments.
|
|
229
|
+
Returns the HTML string to display, or None if not in a notebook.
|
|
230
|
+
"""
|
|
231
|
+
from bokeh.embed import components
|
|
232
|
+
from bokeh.io.state import curstate
|
|
233
|
+
|
|
234
|
+
state = curstate()
|
|
235
|
+
|
|
236
|
+
if not state.notebook:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
if self.ui is None:
|
|
240
|
+
return '<div style="color: red; padding: 10px; border: 1px solid red;">Showable object with no UI set</div>'
|
|
241
|
+
|
|
242
|
+
if self._notebook_rendering:
|
|
243
|
+
# Return a lightweight reference instead of re-rendering the full GUI
|
|
244
|
+
return f'''
|
|
245
|
+
<div style="padding: 10px; background: #f0f8f0; border-left: 4px solid #4CAF50; margin: 5px 0;">
|
|
246
|
+
<strong>↑ iclean GUI active above</strong>
|
|
247
|
+
<small style="color: #666; display: block; margin-top: 5px;">
|
|
248
|
+
Showable ID: {self.id[-8:]} | Backend: Running
|
|
249
|
+
</small>
|
|
250
|
+
</div>
|
|
251
|
+
'''
|
|
252
|
+
|
|
253
|
+
# Apply notebook sizing for Jupyter context
|
|
254
|
+
if self._notebook_sizing == 'fixed':
|
|
255
|
+
self.sizing_mode = None
|
|
256
|
+
self.width = self._notebook_width
|
|
257
|
+
self.height = self._notebook_height
|
|
258
|
+
|
|
259
|
+
script, div = components(self)
|
|
260
|
+
if start_backend:
|
|
261
|
+
self._start_backend()
|
|
262
|
+
|
|
263
|
+
self._notebook_rendering = f'{script}\n{div}'
|
|
264
|
+
return self._notebook_rendering
|
|
265
|
+
|
|
266
|
+
def _repr_html_(self, start_backend=True):
|
|
267
|
+
"""
|
|
268
|
+
HTML representation for Jupyter display.
|
|
269
|
+
|
|
270
|
+
Note: Bokeh doesn't reliably support automatic display via _repr_mimebundle_.
|
|
271
|
+
This provides a helpful message directing users to use show().
|
|
272
|
+
"""
|
|
273
|
+
html = self._get_notebook_html(start_backend)
|
|
274
|
+
|
|
275
|
+
if html is not None:
|
|
276
|
+
return html
|
|
277
|
+
|
|
278
|
+
# Not in notebook environment
|
|
279
|
+
return f"<!-- error: non-notebook environment{' in ' + self.name if self.name else ''} -->" + '''
|
|
280
|
+
<div style="padding: 15px; border: 2px solid #4CAF50; border-radius: 5px; background: #f9fff9; margin: 10px 0;">
|
|
281
|
+
<strong>📊 Showable Widget Ready</strong><br>
|
|
282
|
+
<em>Notebook display is not enabled, run:</em>
|
|
283
|
+
<p><pre>
|
|
284
|
+
from bokeh.io import output_notebook
|
|
285
|
+
output_notebook()</pre>
|
|
286
|
+
<p><em>and try again.</em>
|
|
287
|
+
<hr>
|
|
288
|
+
<small>Contains: {}</small>
|
|
289
|
+
</div>
|
|
290
|
+
'''.format(type(self.ui).__name__ if self.ui else "None")
|
|
291
|
+
|
|
292
|
+
def _repr_mimebundle_(self, include=None, exclude=None):
|
|
293
|
+
"""
|
|
294
|
+
MIME bundle representation for Jupyter display.
|
|
295
|
+
|
|
296
|
+
This is called by Bokeh's show() function and IPython's display system.
|
|
297
|
+
By implementing this, we ensure consistent display whether the object
|
|
298
|
+
is displayed via:
|
|
299
|
+
- Automatic display (just evaluating the object)
|
|
300
|
+
- IPython display(showable)
|
|
301
|
+
- Bokeh show(showable)
|
|
302
|
+
- showable.show()
|
|
303
|
+
"""
|
|
304
|
+
from bokeh.io.state import curstate
|
|
305
|
+
|
|
306
|
+
state = curstate()
|
|
307
|
+
|
|
308
|
+
if state.notebook:
|
|
309
|
+
html = self._get_notebook_html(start_backend=True)
|
|
310
|
+
if html:
|
|
311
|
+
return {
|
|
312
|
+
'text/html': html
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# Fall back to default Bokeh behavior for non-notebook environments
|
|
316
|
+
# Return None to let Bokeh handle it
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
def show(self, start_backend=True):
|
|
320
|
+
"""Explicitly show this Showable using inline display in Jupyter"""
|
|
321
|
+
from bokeh.io.state import curstate
|
|
322
|
+
|
|
323
|
+
self._ensure_in_document()
|
|
324
|
+
|
|
325
|
+
state = curstate()
|
|
326
|
+
|
|
327
|
+
if state.notebook:
|
|
328
|
+
# In Jupyter, display directly using IPython.display
|
|
329
|
+
from IPython.display import display, HTML
|
|
330
|
+
|
|
331
|
+
html = self._get_notebook_html(start_backend)
|
|
332
|
+
if html:
|
|
333
|
+
display(HTML(html))
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
# Fall back to standard Bokeh show for non-notebook environments
|
|
337
|
+
from bokeh.io import show as bokeh_show
|
|
338
|
+
if start_backend:
|
|
339
|
+
self._start_backend()
|
|
340
|
+
bokeh_show(self)
|
|
341
|
+
|
|
342
|
+
def __str__(self):
|
|
343
|
+
"""String conversion"""
|
|
344
|
+
name = f", name='{self.name}'" if self.name else ""
|
|
345
|
+
return f"{self.__class__.__name__}(id='{self.id}'{name} ...)"
|
|
346
|
+
|
|
347
|
+
def __repr__(self):
|
|
348
|
+
"""String representation from repr(...)"""
|
|
349
|
+
ui_type = type(self.ui).__name__ if self.ui else "None"
|
|
350
|
+
doc_info = f"doc='{id(self.document)}'" if self.document else "doc=None"
|
|
351
|
+
backend_info = f"backend='{'started' if getattr(self, '_backend_startup_count', 0) else 'not-started'}'"
|
|
352
|
+
return f"{self.__class__.__name__}(id='{self.id}', name='{self.name}', ui='{ui_type}', {doc_info}, {backend_info})"
|
cubevis/bokeh/models/_tip.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from bokeh.models import Tooltip
|
|
2
2
|
from bokeh.models.layouts import LayoutDOM, UIElement
|
|
3
3
|
from bokeh.core.properties import Instance, Required, Float, Int, Either
|
|
4
|
+
from .. import BokehInit
|
|
4
5
|
|
|
5
|
-
class Tip(LayoutDOM):
|
|
6
|
+
class Tip(LayoutDOM,BokehInit):
|
|
6
7
|
'''Display a tooltip for the child element
|
|
7
8
|
'''
|
|
8
9
|
|
|
@@ -6,10 +6,9 @@ from bokeh.models.widgets.buttons import AbstractButton
|
|
|
6
6
|
from bokeh.models.ui.icons import BuiltinIcon
|
|
7
7
|
from bokeh.core.properties import Instance, Required, Override, Nullable, Float, Int, Either
|
|
8
8
|
from bokeh.models import Tooltip
|
|
9
|
+
from .. import BokehInit
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TipButton(AbstractButton):
|
|
11
|
+
class TipButton(AbstractButton,BokehInit):
|
|
13
12
|
""" A button with a help symbol that displays additional text when hovered
|
|
14
13
|
over or clicked.
|
|
15
14
|
"""
|
|
@@ -44,8 +44,9 @@ from bokeh.models.callbacks import Callback
|
|
|
44
44
|
|
|
45
45
|
from ...utils import serialize, deserialize
|
|
46
46
|
from ..state import casalib_url, cubevisjs_url
|
|
47
|
+
from .. import BokehInit
|
|
47
48
|
|
|
48
|
-
class DataPipe(DataSource):
|
|
49
|
+
class DataPipe(DataSource,BokehInit):
|
|
49
50
|
"""This class allows for communication between Python and the JavaScript implementation
|
|
50
51
|
running in a browser. It allows Python code to send a message to JavaScript and register
|
|
51
52
|
a callback which will receive the result. JavaScript code can do the same to request
|
|
@@ -75,7 +76,10 @@ class DataPipe(DataSource):
|
|
|
75
76
|
_active_sessions = {} # session_id -> {'websocket': ws, 'timestamp': time, 'datapipe': instance}
|
|
76
77
|
_session_lock = threading.Lock()
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
###################################################################
|
|
80
|
+
### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
|
|
81
|
+
###################################################################
|
|
82
|
+
#__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
79
83
|
|
|
80
84
|
def __init__( self, *args, abort=None, **kwargs ):
|
|
81
85
|
super( ).__init__( *args, **kwargs )
|
|
@@ -34,8 +34,9 @@ from bokeh.core.properties import Instance, Tuple, Int, Nullable
|
|
|
34
34
|
from bokeh.models.callbacks import Callback
|
|
35
35
|
from ._image_pipe import ImagePipe
|
|
36
36
|
from ..state import casalib_url, cubevisjs_url
|
|
37
|
+
from .. import BokehInit
|
|
37
38
|
|
|
38
|
-
class ImageDataSource(ColumnDataSource):
|
|
39
|
+
class ImageDataSource(ColumnDataSource,BokehInit):
|
|
39
40
|
"""Implementation of a ``ColumnDataSource`` customized for planes from
|
|
40
41
|
`CASA`/`CNGI` image cubes. This is designed to use an `ImagePipe` to
|
|
41
42
|
update the image channel/plane displayed in a browser, app or notebook
|
|
@@ -59,7 +60,10 @@ class ImageDataSource(ColumnDataSource):
|
|
|
59
60
|
num_chans = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
|
|
60
61
|
cur_chan = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
###################################################################
|
|
64
|
+
### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
|
|
65
|
+
###################################################################
|
|
66
|
+
#__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
63
67
|
|
|
64
68
|
def __init__( self, *args, **kwargs ):
|
|
65
69
|
super( ).__init__( *args, **kwargs )
|
|
@@ -85,7 +85,10 @@ class ImagePipe(DataPipe):
|
|
|
85
85
|
data source for (raw) image channel histogram of intensities used with a "figure.quad(...)"
|
|
86
86
|
''')
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
###################################################################
|
|
89
|
+
### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
|
|
90
|
+
###################################################################
|
|
91
|
+
#__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
89
92
|
|
|
90
93
|
def __open_image( self, image ):
|
|
91
94
|
if self.__img is not None:
|
|
@@ -32,9 +32,9 @@ from bokeh.util.compiler import TypeScript
|
|
|
32
32
|
from bokeh.core.properties import Instance
|
|
33
33
|
from . import ImagePipe
|
|
34
34
|
from ..state import casalib_url, cubevisjs_url
|
|
35
|
+
from .. import BokehInit
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
class SpectraDataSource(ColumnDataSource):
|
|
37
|
+
class SpectraDataSource(ColumnDataSource,BokehInit):
|
|
38
38
|
"""Implementation of a ``ColumnDataSource`` customized for spectral lines
|
|
39
39
|
from `CASA`/`CNGI` image cubes. This is designed to use an `ImagePipe` to
|
|
40
40
|
update the spectral line plot displayed in a browser, app or notebook with
|
|
@@ -48,7 +48,10 @@ class SpectraDataSource(ColumnDataSource):
|
|
|
48
48
|
|
|
49
49
|
image_source = Instance(ImagePipe)
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
###################################################################
|
|
52
|
+
### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
|
|
53
|
+
###################################################################
|
|
54
|
+
#__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
52
55
|
|
|
53
56
|
def __init__( self, *args, **kwargs ):
|
|
54
57
|
super( ).__init__( *args, **kwargs )
|
|
@@ -38,8 +38,9 @@ import websockets
|
|
|
38
38
|
from ._data_pipe import DataPipe
|
|
39
39
|
from ..state import casalib_url, cubevisjs_url
|
|
40
40
|
from ...utils import find_ws_address
|
|
41
|
+
from .. import BokehInit
|
|
41
42
|
|
|
42
|
-
class UpdatableDataSource(ColumnDataSource):
|
|
43
|
+
class UpdatableDataSource(ColumnDataSource,BokehInit):
|
|
43
44
|
"""Implementation of a `ColumnDataSource` customized to allow updates from Python via
|
|
44
45
|
WebSockets after the Bokeh GUI has been displayed. This class is derived from `ColumnDataSource`
|
|
45
46
|
and provides an `update` function that can be used to update the plotted data. While this
|
|
@@ -87,7 +88,10 @@ class UpdatableDataSource(ColumnDataSource):
|
|
|
87
88
|
Internal id used for communcations.
|
|
88
89
|
""" )
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
###################################################################
|
|
92
|
+
### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
|
|
93
|
+
###################################################################
|
|
94
|
+
#__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
91
95
|
|
|
92
96
|
__created_pipe = False
|
|
93
97
|
__id = str(uuid4( ))
|
cubevis/bokeh/state/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
########################################################################
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2022
|
|
3
|
+
# Copyright (C) 2022,2025
|
|
4
4
|
# Associated Universities, Inc. Washington DC, USA.
|
|
5
5
|
#
|
|
6
6
|
# This script is free software; you can redistribute it and/or modify it
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
'''Bokeh state management functions (both within the Bokeh distribution
|
|
29
29
|
and with the Bokeh extensions found in ``cubevis.bokeh``.'''
|
|
30
30
|
|
|
31
|
-
from ._initialize import
|
|
31
|
+
from ._initialize import order_bokeh_js, register_model, set_cubevis_lib, get_js_loading, set_js_loading, get_js_loading_selection, JsLoading
|
|
32
32
|
from ._session import setup_session as initialize_session
|
|
33
33
|
from ._palette import available_palettes, find_palette, default_palette
|
|
34
|
-
from ._javascript import casalib_url, cubevisjs_url
|
|
34
|
+
from ._javascript import casalib_path, casalib_url, cubevisjs_path, cubevisjs_url
|
|
35
|
+
from ._current import CurrentBokehState as current_bokeh_state
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import FrozenSet
|
|
3
|
+
from bokeh import __version__ as _bokeh_version
|
|
4
|
+
from bokeh.io.state import curstate
|
|
5
|
+
|
|
6
|
+
class BokehMode(Enum):
|
|
7
|
+
NOTEBOOK = "jupyter_notebook"
|
|
8
|
+
FILE = "file_output"
|
|
9
|
+
BROWSER = "browser_display"
|
|
10
|
+
SERVER = "bokeh_server"
|
|
11
|
+
|
|
12
|
+
class CurrentBokehState:
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def version( ) -> str:
|
|
16
|
+
return _bokeh_version
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def mode( ) -> FrozenSet[str]:
|
|
20
|
+
state = curstate( )
|
|
21
|
+
|
|
22
|
+
notebook = getattr(state, '_notebook', False)
|
|
23
|
+
file = getattr(state, '_file', None) is not None
|
|
24
|
+
server = getattr(state, '_server_enabled', False)
|
|
25
|
+
browser = file or not (notebook or server)
|
|
26
|
+
|
|
27
|
+
return frozenset(
|
|
28
|
+
mode for mode, condition in [
|
|
29
|
+
(BokehMode.NOTEBOOK, notebook),
|
|
30
|
+
(BokehMode.FILE, file),
|
|
31
|
+
(BokehMode.SERVER, server),
|
|
32
|
+
(BokehMode.BROWSER, browser),
|
|
33
|
+
] if condition
|
|
34
|
+
)
|