cubevis 1.0.8__tar.gz → 1.0.15__tar.gz
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-1.0.8 → cubevis-1.0.15}/PKG-INFO +1 -1
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__js__/bokeh-3.8/cubevisjs.min.js +9 -8
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/__init__.py +1 -0
- cubevis-1.0.15/cubevis/bokeh/models/_bokeh_app_context.py +63 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/_data_pipe.py +7 -1
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_createmask.py +41 -33
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_createregion.py +29 -21
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_cube.py +20 -18
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_interactive_clean_ui.mustache +61 -44
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_interactive_clean_ui.py +61 -44
- {cubevis-1.0.8 → cubevis-1.0.15}/pyproject.toml +2 -2
- cubevis-1.0.15/tests/manual/iclean-demo/iclean-demo.ipynb +251 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-outlier/run-iclean.py +2 -3
- {cubevis-1.0.8 → cubevis-1.0.15}/LICENSE +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/README.rst +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/LICENSE.rst +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/20px/fast-backward.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/20px/fast-forward.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/20px/step-backward.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/20px/step-forward.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/add-chan.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/add-chan.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/add-cube.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/add-cube.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/drag.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/drag.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/mask-selected.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/mask.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/mask.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/new-layer-sm-selected.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/new-layer-sm-selected.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/new-layer-sm.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/new-layer-sm.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/reset.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/reset.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/sub-chan.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/sub-chan.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/sub-cube.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/sub-cube.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/trash.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/trash.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/trash_full.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/trash_full.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/trash_full_raw.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/zoom-to-fit.png +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__icons__/zoom-to-fit.svg +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__js__/bokeh-3.6/cubevisjs.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__js__/bokeh-3.7/cubevisjs.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/__js__/casalib.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/annotations/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/annotations/_ev_poly_annotation.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/components/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/format/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/format/_time_ticks.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/format/_wcs_ticks.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_edit_span.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_ev_text_input.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_shared_dict.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_showable.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_tip.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/models/_tip_button.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/_image_data_source.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/_image_pipe.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/_spectra_data_source.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/sources/_updatable_data_source.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/_current.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/_initialize.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/_javascript.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/_palette.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/_session.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/bokeh-2.4.1.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/state/js/casalib-v0.0.1.min.js +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/tools/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/tools/_cbreset_tool.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/tools/_drag_tool.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/utils/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/utils/_axes_labels.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/bokeh/utils/_svg_icon.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/data/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/data/casaimage/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/exe/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/exe/_context.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/exe/_mode.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/exe/_setting.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/exe/_task.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/_gclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_interactiveclean.mustache +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_interactiveclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_interactivecleannotebook.mustache +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_interactivecleannotebook.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_plotants.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/apps/_plotbandpass.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casashell/createmask.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casashell/iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casatasks/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casatasks/createmask.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casatasks/createregion.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casatasks/iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/private/casatasks/iclean_notebook.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/readme.rst +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/remote/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/remote/_gclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/remote/_local.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/remote/_remote_kernel.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_app_context.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_interactiveclean_wrappers.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/toolbox/_region_list.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_ResourceManager.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/__init__.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_browser.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_contextmgrchain.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_conversion.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_copydoc.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_docenum.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_git.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_import_protected_module.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_jupyter.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_logging.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_pkgs.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_regions.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_static.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/cubevis/utils/_tiles.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/alma-many-chan/alma-many-chan.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/basic-websockets-demo/client.html +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/basic-websockets-demo/client.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/basic-websockets-demo/server.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/createmask-demo/run-createmask.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/createregion-demo/run-createregion.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/cubemask-demo/image-slider-spectra-done-stats.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/cubemask-demo/image-slider-spectra-done.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/cubemask-demo/image-slider-spectra.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/cubemask-demo/image-slider.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/cubemask-demo/image.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/m100_interactive.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/mask0-iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/run-gclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/run-iclean-obj.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/run-iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-demo/vla-sim-jet-iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-first-look/run-fl-cont.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-first-look/run-fl-line.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-outlier/test_outlier.txt +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/iclean-remote/iclean_remote_webserver.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/large-cube/run-largecube.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/svg-test.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/updatable-data-source/direct-plot.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/updatable-data-source/simple-update.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/updatable-data-source/updated-plot.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/uranus-demo/uranus-iclean.py +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/websocket-reconnect/client.html +0 -0
- {cubevis-1.0.8 → cubevis-1.0.15}/tests/manual/websocket-reconnect/server.py +0 -0
|
@@ -42,23 +42,24 @@
|
|
|
42
42
|
}
|
|
43
43
|
})
|
|
44
44
|
({
|
|
45
|
-
"
|
|
46
|
-
"
|
|
45
|
+
"e2864cf297": function _(e,t,a,o,c){o();const n=e("tslib"),i=e("87aeec7876");c("DataPipe",i.DataPipe);const p=e("2889e0dd45");c("ImagePipe",p.ImagePipe);const s=e("de65005924");c("ImageDataSource",s.ImageDataSource);const r=e("02e3c3e46c");c("SpectraDataSource",r.SpectraDataSource);const S=e("64b16deff9");c("UpdatableDataSource",S.UpdatableDataSource);const l=e("b6ae454f0d");c("WcsTicks",l.WcsTicks);const d=e("cb7d28d6b3");c("DragTool",d.DragTool);const D=e("9d3c34ff8e");c("CBResetTool",D.CBResetTool);const T=e("e3901fa9f2");c("serialize",T.serialize),c("deserialize",T.deserialize);const u=e("9f961622ce");c("TipButton",u.TipButton);const b=e("ca4c845905");c("Tip",b.Tip);const f=e("5e5f767c53");c("Showable",f.Showable);const g=e("467d2716b0");c("BokehAppContext",g.BokehAppContext);const h=e("50a1e32f01");c("SharedDict",h.SharedDict);const B=e("b55081402e");c("EditSpan",B.EditSpan);const E=e("9144bfc7a5");c("EvTextInput",E.EvTextInput);const I=e("74e0abef8a");c("EvPolyAnnotation",I.EvPolyAnnotation);const P=n.__importStar(e("389c724eb9"));a.find=P;(0,e("@bokehjs/base").register_models)({DataPipe:i.DataPipe,ImagePipe:p.ImagePipe,ImageDataSource:s.ImageDataSource,SpectraDataSource:r.SpectraDataSource,UpdatableDataSource:S.UpdatableDataSource,WcsTicks:l.WcsTicks,DragTool:d.DragTool,CBResetTool:D.CBResetTool,Tip:b.Tip,TipButton:u.TipButton,SharedDict:h.SharedDict,Showable:f.Showable,BokehAppContext:g.BokehAppContext,EditSpan:B.EditSpan,EvTextInput:E.EvTextInput,EvPolyAnnotation:I.EvPolyAnnotation})},
|
|
46
|
+
"87aeec7876": function _(e,s,t,i,n){var o;i();const c=e("@bokehjs/models/sources/data_source"),a=e("e3901fa9f2"),l=e("@bokehjs/core/util/callbacks");class r extends c.DataSource{constructor(e){super(e),this.send_queue={},this.connection_queue=[],this.pending={},this.incoming_callbacks={},this.session_id=casalib.object_id(this)}checkSessionConflict(){try{if("undefined"==typeof Storage)return console.warn("localStorage not available, skipping session conflict detection"),!0;const e=localStorage.getItem(this.session_storage_key);if(e){const s=JSON.parse(e);if(s.sessionId!==this.session_id&&Date.now()-s.timestamp<12e4){if(this.conflict_check){const e=`CubeVis DataPipe (${this.instance_key}) is already running in another browser window or tab.\n\nPlease close other instances and refresh this page, or\nclose this window to continue using the other instance.`;return alert(e),window.opener||1===window.history.length?window.close():window.location.href="about:blank",!1}console.group(`DataPipe ${this.instance_key} conflict detected in Jupyter context`),console.log("Current session ID:",this.session_id),console.log("Existing session ID:",s.sessionId),console.log("Existing timestamp:",new Date(s.timestamp).toISOString()),console.log("Age of existing session (ms):",Date.now()-s.timestamp),console.log("Address:",this.address),console.log("Instance key:",this.instance_key),console.log("Storage key:",this.session_storage_key),console.log("Existing data:",s),console.log("All localStorage keys:",Object.keys(localStorage).filter((e=>e.startsWith("cubevis_datapipe_")))),console.groupEnd()}}return this.updateSessionHeartbeat(),!0}catch(e){return console.warn("Session conflict detection failed:",e),!0}}updateSessionHeartbeat(){try{"undefined"!=typeof Storage&&localStorage.setItem(this.session_storage_key,JSON.stringify({sessionId:this.session_id,timestamp:Date.now(),instanceKey:this.instance_key}))}catch(e){console.warn("Session heartbeat update failed:",e)}}startHeartbeat(){this.heartbeat_interval=window.setInterval((()=>{this.updateSessionHeartbeat()}),3e4)}stopHeartbeat(){this.heartbeat_interval&&(clearInterval(this.heartbeat_interval),this.heartbeat_interval=void 0)}cleanupSession(){try{if("undefined"!=typeof Storage){const e=localStorage.getItem(this.session_storage_key);if(e){JSON.parse(e).sessionId===this.session_id&&localStorage.removeItem(this.session_storage_key)}}}catch(e){console.warn("Session cleanup failed:",e)}this.stopHeartbeat()}handleSessionConflictMessage(e){console.error("Session conflict detected by server:",e);let s="Session conflict detected by server.";"session_conflict"===e.type?s=e.error||s:"session_corruption"===e.type&&(s=`Session corruption detected.\nExpected: ${e.expected}\nReceived: ${e.received}`),alert(s+"\n\nThis window will be closed to prevent data corruption."),this.cleanupSession();const t=new CustomEvent("cubevis_session_conflict",{detail:{message:e,sessionId:this.session_id}});window.dispatchEvent(t),setTimeout((()=>{window.opener||1===window.history.length?window.close():window.location.href="about:blank"}),2e3)}generateInstanceKey(){const e=`${this.address[0]}_${this.address[1]}`;return`${this.instance_id}_${e}`}initialize(){if(super.initialize(),console.log("**** TypeScript initialize ****"),console.log("conflict_check value:",this.conflict_check),console.log("conflict_check type:",typeof this.conflict_check),console.log("All properties:",this.properties),console.log("*******************************"),this.instance_key=this.generateInstanceKey(),this.session_storage_key=`cubevis_datapipe_${this.instance_key}`,!this.checkSessionConflict())return;let e=`ws://${this.address[0]}:${this.address[1]}`;console.log("datapipe url:",e);var s=void 0;document.shutdown_in_progress_=!1;var t=()=>{void 0!==this.websocket&&this.websocket.close(),this.websocket=new WebSocket(e),this.websocket.binaryType="arraybuffer",this.websocket.addEventListener("error",(e=>{console.log("error encountered:",e)})),this.websocket.onmessage=e=>{if("string"==typeof e.data||e.data instanceof String){let s=(0,a.deserialize)(e.data);if("id"in s&&"direction"in s&&"message"in s){let{id:e,message:t,direction:i}=s;if("error"===i&&("session_conflict"===e||e===this.session_id)&&t&&("session_conflict"===t.type||"session_corruption"===t.type||"close_duplicate"===t.action))return void this.handleSessionConflictMessage(t);if(void 0===t&&console.log("Error, event failure",s),"j2p"==i)if(e in this.pending){let{cb:i}=this.pending[e];if(delete this.pending[e],e in this.send_queue&&this.send_queue[e].length>0){let{cb:s,msg:t}=this.send_queue[e].shift();this.pending[e]={cb:s},this.websocket.send((0,a.serialize)(t))}void 0===t?console.log("DROPPING ERROR FOR NOW (maybe need error callbacks)",s):i(t)}else console.log("message received but could not find id");else if(e in this.incoming_callbacks){let s=this.incoming_callbacks[e](t);this.websocket.send((0,a.serialize)({id:e,direction:i,message:s,session:this.session_id}))}}else console.log(`datapipe received message without one of 'id', 'message' or 'direction': ${s}`)}else console.log("datapipe received binary data",e.data.byteLength,"bytes")},this.websocket.onopen=()=>{for(s?0==s.connected&&console.log(`connection reestablished at ${new Date}`):(this.websocket.send((0,a.serialize)({id:"initialize",direction:"j2p",session:this.session_id})),this.startHeartbeat()),s=new casalib.ReconnectState;this.connection_queue.length>0;){let e=this.connection_queue.shift();this.send.apply(e[0],e[1])}},this.websocket.onclose=()=>{if(s&&1==s.connected&&(console.log(`connection lost at ${new Date}`),s.connected=!1,!document.shutdown_in_progress_)){console.log(`connection lost at ${new Date}`);var e=s;function i(n){0==s.connected&&(console.log(`${n+1}\treconnection attempt ${new Date}`),t(),e.backoff(),e.retries>0?setTimeout(i,e.timeout,n+1):0==s.connected&&console.log(`aborting reconnection after ${n} attempts ${new Date}`))}i(0)}}};window.addEventListener("beforeunload",(()=>{this.cleanupSession()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?this.stopHeartbeat():"visible"===document.visibilityState&&(this.updateSessionHeartbeat(),this.startHeartbeat())})),t();(()=>{null!=this.init_script&&(0,l.execute)(this.init_script,this)})()}register(e,s){this.incoming_callbacks[e]=s}send(e,s,t,i=!1){let n={id:e,message:s,direction:"j2p",session:this.session_id};if(!this.websocket||e in this.pending)if(e in this.send_queue)if("boolean"==typeof i&&i&&this.send_queue[e].length>0)this.send_queue[e][0].msg=n,this.send_queue[e][0].cb=t;else if("function"==typeof i&&this.send_queue[e].length>0){let o=!1;for(const c of this.send_queue[e])i(c.msg.message)&&(c.msg=n,c.cb=t,o=!0);o||this.send_queue[e].push({cb:t,msg:n})}else this.send_queue[e].push({cb:t,msg:n});else this.send_queue[e]=[{cb:t,msg:n}];else if(this.websocket.readyState===WebSocket.CONNECTING)this.connection_queue.push([this,[e,s,t]]);else if(e in this.send_queue&&this.send_queue[e].length>0){this.send_queue[e].push({cb:t,msg:n});{let{cb:l,msg:r}=this.send_queue[e].shift();if(this.pending[e]={cb:l},this.websocket.readyState===WebSocket.OPEN)this.websocket.send((0,a.serialize)(r));else{let d=20,h=this;function u(){h.websocket.readyState===WebSocket.OPEN?h.websocket.send((0,a.serialize)(r)):(d-=1,d>0&&setTimeout(u,3e3))}setTimeout(u,3e3)}}}else if(this.websocket.readyState===WebSocket.OPEN)this.pending[e]={cb:t},this.websocket.send((0,a.serialize)(n));else{let g=20,_=this;function b(){_.websocket.readyState===WebSocket.OPEN?(_.pending[e]={cb:t},_.websocket.send((0,a.serialize)(n))):(g-=1,g>0&&setTimeout(b,3e3))}setTimeout(b,3e3)}}}t.DataPipe=r,o=r,r.__name__="DataPipe",r.__module__="cubevis.bokeh.sources._data_pipe",o.define((({Any:e,Tuple:s,String:t,Number:i,Bool:n})=>({init_script:[e,null],address:[s(t,i)],instance_id:[t],conflict_check:[n,!0]})))},
|
|
47
47
|
"e3901fa9f2": function _(e,r,s,i,o){i();const l=e("@bokehjs/base"),a=e("@bokehjs/core/resolvers"),t=e("@bokehjs/core/serialization/deserializer"),n=e("@bokehjs/core/serialization/serializer"),{deserialize:c}=new class{constructor(){this.resolver=new a.ModelResolver(l.default_resolver),this.deserializer=new t.Deserializer(this.resolver),this.deserialize=e=>{try{return this.deserializer.decode(JSON.parse(e))}catch(r){return console.group("deserialize error"),console.log(e),console.log(r),console.groupEnd(),{}}}}};s.deserialize=c;const{serialize:z}=new class{constructor(){this.serializer=new n.Serializer,this.serialize=e=>JSON.stringify(this.serializer.encode(e))}};s.serialize=z},
|
|
48
|
-
"2889e0dd45": function _(i,s,
|
|
48
|
+
"2889e0dd45": function _(i,e,s,t,n){var a;t();const o=i("@bokehjs/models/sources/column_data_source"),d=i("87aeec7876");class r extends d.DataPipe{constructor(i){super(i),this.position={},this._wcs=null}initialize(){super.initialize(),this.fits_header_json&&(this._wcs=new casalib.coordtxl.WCSTransform(new casalib.coordtxl.MapKeywordProvider(JSON.parse(this.fits_header_json))))}channel(i,e,s){this.position[s]={index:i};let t={action:"channel",index:i,id:s};super.send(this.dataid,t,(i=>{null!=this._histogram_source&&"hist"in i&&"top"in i.hist&&"bottom"in i.hist&&"left"in i.hist&&"right"in i.hist&&(this._histogram_source.data=i.hist),e(i)}))}spectrum(i,e,s,t=!1){let n={action:"spectrum",index:i,id:s};super.send(this.dataid,n,e,t)}adjust_colormap(i,e,s,t,n=!1){const a={action:"adjust-colormap",bounds:i,transfer:e,id:t};super.send(this.dataid,a,s,n)}refresh(i,e,s=[0,0]){let{index:t}=e in this.position?this.position[e]:{index:s};if(2===t.length){let s={action:"channel",index:t,id:e};super.send(this.dataid,s,i)}else if(3===t.length){let s={action:"spectrum",index:t,id:e};super.send(this.dataid,s,i)}}wcs(){return this._wcs}}s.ImagePipe=r,a=r,r.__name__="ImagePipe",r.__module__="cubevis.bokeh.sources._image_pipe",a.define((({Number:i,Nullable:e,String:s,Tuple:t,Ref:n})=>({dataid:[s],shape:[t(i,i,i,i)],fits_header_json:[e(s),null],_histogram_source:[e(n(o.ColumnDataSource)),null]})))},
|
|
49
49
|
"de65005924": function _(s,a,t,c,i){var o;c();const e=s("@bokehjs/models/sources/column_data_source"),n=s("@bokehjs/core/util/string"),u=s("2889e0dd45"),h=s("@bokehjs/core/util/callbacks");class r extends e.ColumnDataSource{constructor(s){super(s),this.imid=(0,n.uuid4)()}_mask_contour(s){const a=casalib.d3.contours().size(this.image_source.shape.slice(0,2)).thresholds([1])(s[0])[0].coordinates.map((s=>s.map((s=>s.reduce(((s,a)=>(s[0].push(a[0]),s[1].push(a[1]),s)),[[],[]])))));return{xs:[a.map((s=>s.map((s=>s[0]))))],ys:[a.map((s=>s.map((s=>s[1]))))]}}initialize(){if(super.initialize(),null!=this._mask_contour_source&&"msk"in this.data&&this.data.msk.length>0&&this.data.msk[0].length>0){const s=this.data.msk;this._mask_contour_source.data=this._mask_contour(s)}void 0===this.last_chan&&(this.last_chan=[this.cur_chan[0].valueOf(),this.cur_chan[1].valueOf()]);(()=>{null!=this.init_script&&(0,h.execute)(this.init_script,this)})()}channel(s,a=0,t){this.image_source.channel([a,s],(c=>{void 0!==c&&void 0!==c.chan||console.log("ImageDataSource ERROR ENCOUNTERED <1>",c),this.last_chan=[this.cur_chan[0].valueOf(),this.cur_chan[1].valueOf()],this.cur_chan=[a,s],null!=this._mask_contour_source&&"chan"in c&&"msk"in c.chan&&(c.msk_contour=this._mask_contour(c.chan.msk),this._mask_contour_source.data=c.msk_contour),t&&t(c),this.data=c.chan}),this.imid)}adjust_colormap(s,a,t){this.image_source.adjust_colormap(s,a,t,this.imid,!0)}signal_change(){this.change.emit()}refresh(s){this.image_source.refresh((a=>{void 0!==a&&void 0!==a.chan||console.log("ImageDataSource ERROR ENCOUNTERED <2>",a),null!=this._mask_contour_source&&"chan"in a&&"msk"in a.chan&&(a.msk_contour=this._mask_contour(a.chan.msk),this._mask_contour_source.data=a.msk_contour),s&&s(a),this.data=a.chan}),this.imid,[0,0])}wcs(){return this.image_source.wcs()}}t.ImageDataSource=r,o=r,r.__name__="ImageDataSource",r.__module__="cubevis.bokeh.sources._image_data_source",o.define((({Tuple:s,Number:a,Ref:t,Nullable:c,Any:i})=>({init_script:[i,null],image_source:[t(u.ImagePipe)],_mask_contour_source:[c(t(e.ColumnDataSource)),null],num_chans:[s(a,a)],cur_chan:[s(a,a)]})))},
|
|
50
50
|
"02e3c3e46c": function _(e,s,i,t,r){var a;t();const c=e("@bokehjs/models/sources/column_data_source"),u=e("@bokehjs/core/util/string"),o=e("2889e0dd45");class _ extends c.ColumnDataSource{constructor(e){super(e),this.imid=(0,u.uuid4)()}initialize(){super.initialize()}spectra(e,s,i=0,t=!1){this.image_source.spectrum([e,s,i],(e=>this.data=e.spectrum),this.imid,t)}refresh(){this.image_source.refresh((e=>this.data=e.spectrum),this.imid,[0,0,0])}}i.SpectraDataSource=_,a=_,_.__name__="SpectraDataSource",_.__module__="cubevis.bokeh.sources._spectra_data_source",a.define((({Ref:e})=>({image_source:[e(o.ImagePipe)]})))},
|
|
51
|
-
"64b16deff9": function _(e,s,i,t,a){var n;t();const u=e("@bokehjs/models/sources/column_data_source"),l=e("
|
|
51
|
+
"64b16deff9": function _(e,s,i,t,a){var n;t();const u=e("@bokehjs/models/sources/column_data_source"),l=e("87aeec7876"),o=e("@bokehjs/core/util/callbacks");class c extends u.ColumnDataSource{constructor(e){super(e)}send(e,s){this.pipe.send(this.session_id.valueOf(),{action:"callback",message:e},(e=>{s("result"in e?e.result:{error:`expected to find a "result" in "${e}"`,msg:e})}))}initialize(){super.initialize();(()=>{null!=this.js_init&&(0,o.execute)(this.js_init,this)})()}}i.UpdatableDataSource=c,n=c,c.__name__="UpdatableDataSource",c.__module__="cubevis.bokeh.sources._updatable_data_source",n.define((({Ref:e,Any:s,String:i})=>({js_init:[s,null],js_update:[s,null],pipe:[e(l.DataPipe)],session_id:[i]})))},
|
|
52
52
|
"b6ae454f0d": function _(s,i,o,t,e){var r;t();const a=s("@bokehjs/models/formatters/tick_formatter"),c=s("de65005924");class l extends a.TickFormatter{constructor(s){super(s),this._axis=null,this._coord="world"}initialize(){super.initialize(),"x"==this.axis||"X"==this.axis||"y"==this.axis||"Y"==this.axis?this._axis="x"==this.axis||"X"==this.axis?"x":"y":console.log("ERROR: WcsTicks formatter created with invalid axis:",this.axis)}doFormat(s){const i=[];if(this._axis&&this.image_source.wcs()&&"world"==this._coord)for(let o=0,t=s.length;o<t;o++)if("x"==this._axis){const t=new casalib.coordtxl.Point2D(Number(s[o]),0);this.image_source.wcs().imageToWorldCoords(t,!1),i.push(new casalib.coordtxl.WorldCoords(t.getX(),t.getY()).format(2e3)[0])}else{const t=new casalib.coordtxl.Point2D(0,Number(s[o]));this.image_source.wcs().imageToWorldCoords(t,!1),i.push(new casalib.coordtxl.WorldCoords(t.getX(),t.getY()).format(2e3)[1])}else for(let o=0,t=s.length;o<t;o++)i.push(""+s[o]);return i}coordinates(s){return s!=this._coord&&("world"!=s&&"pixel"!=s||(this._coord=s)),this._coord}}o.WcsTicks=l,r=l,l.__name__="WcsTicks",l.__module__="cubevis.bokeh.format._wcs_ticks",r.define((({Ref:s,String:i})=>({axis:[i],image_source:[s(c.ImageDataSource)]})))},
|
|
53
|
-
"cb7d28d6b3": function _(i,
|
|
54
|
-
"
|
|
55
|
-
"
|
|
53
|
+
"cb7d28d6b3": function _(e,i,t,o,s){var d;o();const l=e("@bokehjs/models/tools/gestures/gesture_tool"),r=e("8fc7a9e935"),_=e("389c724eb9"),m=e("@bokehjs/core/util/callbacks");class a extends l.GestureToolView{_pan_start(e){var i;null===(i=this.model.document)||void 0===i||i.interactive_start(this.plot_view.model);const t=(0,_.px_from_sx)(this.plot_view,e.sx),o=(0,_.py_from_sy)(this.plot_view,e.sy),s=(0,_.dx_from_px)(this.plot_view,t),d=(0,_.dy_from_py)(this.plot_view,o),{start:l}=this.model;l?(0,m.execute)(l,this.model,{sx:t,sy:o,x:s,y:d,delta_x:e.dx,delta_y:-e.dy,shift:"modifiers"in e?e.modifiers.shift:void 0,ctrl:"modifiers"in e?e.modifiers.ctrl:void 0,alt:"modifiers"in e?e.modifiers.alt:void 0}):this.model.trigger_event(new r.DragStart(t,o,s,d,e.dx,-e.dy,e.modifiers))}_pan(e){var i;null===(i=this.model.document)||void 0===i||i.interactive_start(this.plot_view.model);const t=(0,_.px_from_sx)(this.plot_view,e.sx),o=(0,_.py_from_sy)(this.plot_view,e.sy),s=(0,_.dx_from_px)(this.plot_view,t),d=(0,_.dy_from_py)(this.plot_view,o),{move:l}=this.model;l?(0,m.execute)(l,this.model,{sx:t,sy:o,x:s,y:d,delta_x:e.dx,delta_y:-e.dy,shift:"modifiers"in e?e.modifiers.shift:void 0,ctrl:"modifiers"in e?e.modifiers.ctrl:void 0,alt:"modifiers"in e?e.modifiers.alt:void 0}):this.model.trigger_event(new r.Drag(t,o,s,d,e.dx,-e.dy,e.modifiers))}_pan_end(e){const i=(0,_.px_from_sx)(this.plot_view,e.sx),t=(0,_.py_from_sy)(this.plot_view,e.sy),o=(0,_.dx_from_px)(this.plot_view,i),s=(0,_.dy_from_py)(this.plot_view,t),{end:d}=this.model;d?(0,m.execute)(d,this.model,{sx:i,sy:t,x:o,y:s,delta_x:e.dx,delta_y:-e.dy,shift:"modifiers"in e?e.modifiers.shift:void 0,ctrl:"modifiers"in e?e.modifiers.ctrl:void 0,alt:"modifiers"in e?e.modifiers.alt:void 0}):this.model.trigger_event(new r.DragEnd(i,t,o,s,e.dx,-e.dy,e.modifiers))}}t.DragToolView=a,a.__name__="DragToolView";class n extends l.GestureTool{constructor(e){super(e),this.tool_name="Drag",this.event_type="pan",this.default_order=10}}t.DragTool=n,d=n,n.__name__="DragTool",n.__module__="cubevis.bokeh.tools._drag_tool",d.prototype.default_view=a,d.define((({Any:e,Nullable:i})=>({start:[i(e),null],move:[i(e),null],end:[i(e),null]})))},
|
|
54
|
+
"8fc7a9e935": function _(e,t,a,s,_){s();const n=e("@bokehjs/core/bokeh_events");class r extends n.Pan{}a.Drag=r,r.__name__="Drag";class l extends n.PanStart{constructor(e,t,a,s,_,n,r){super(e,t,a,s,r),this.delta_x=_,this.delta_y=n}get event_values(){const{delta_x:e,delta_y:t}=this;return{...super.event_values,delta_x:e,delta_y:t}}}a.DragStart=l,l.__name__="DragStart";class d extends n.PanEnd{constructor(e,t,a,s,_,n,r){super(e,t,a,s,r),this.delta_x=_,this.delta_y=n}get event_values(){const{delta_x:e,delta_y:t}=this;return{...super.event_values,delta_x:e,delta_y:t}}}a.DragEnd=d,d.__name__="DragEnd"},
|
|
55
|
+
"389c724eb9": function _(n,o,e,t,r){t(),e.view=function(n){function o(e){if(e.model===n)return e;for(const n of e.children()){const e=o(n);if(e)return e}return null}const e=n.document;if(!e)return null;const t=e.views_manager;if(!t)return null;const r=t.roots;for(const n of r){const e=o(n);if(e)return e}return null},e.span_coords=function(n){function o(n,o,e,t,r){if(null!=n)switch(o){case"canvas":return r.compute(n);case"screen":return t.compute(n);case"data":return e.compute(n)}return NaN}const{frame:e,canvas:t}=n.plot_view,{x_scale:r,y_scale:i}=n.coordinates;let c,u,s,f,_=n.model.dimension;"width"==n.model.dimension?(s=o(n.model.location,n.model.location_units,i,e.bbox.yview,t.bbox.y_screen),u=e.bbox.left,f=e.bbox.width,c=n.model.line_width):(s=e.bbox.top,u=o(n.model.location,n.model.location_units,r,e.bbox.xview,t.bbox.y_screen),f=n.model.line_width,c=e.bbox.height);return{stop:s,sleft:u,width:f,height:c,orientation:_}},e.px_from_sx=function(n,o){return n.frame.bbox.x_view.invert(o)},e.py_from_sy=function(n,o){return n.frame.bbox.y_view.invert(o)},e.dx_from_px=function(n,o){const e=n.frame.bbox.x_view.compute(o);return n.frame.x_scale.invert(e)},e.dy_from_py=function(n,o){const e=n.frame.bbox.y_view.compute(o);return n.frame.y_scale.invert(e)},e.sx_from_dx=function(n,o){return n.frame.x_scale.compute(o)},e.sy_from_dy=function(n,o){return n.frame.y_scale.compute(o)},e.v_px_from_sx=function(n,o){return n.frame.bbox.x_view.v_invert(o)},e.v_py_from_sy=function(n,o){return n.frame.bbox.y_view.v_invert(o)},e.v_dx_from_px=function(n,o){const e=n.frame.bbox.x_view.v_compute(o);return n.frame.x_scale.v_invert(e)},e.v_dy_from_py=function(n,o){const e=n.frame.bbox.y_view.v_compute(o);return n.frame.y_scale.v_invert(e)},e.v_sx_from_dx=function(n,o){return n.frame.x_scale.v_compute(o)},e.v_sy_from_dy=function(n,o){return n.frame.y_scale.v_compute(o)},e.context=u,e.appState=function(n){var o,e;const t=u(n);if(t){return null===(e=(null===(o=null===window||void 0===window?void 0:window.cubevisAppSession)||void 0===o?void 0:o.applications)[t.app_id])||void 0===e?void 0:e.state}return};const i=n("@bokehjs/model");function c(n,o,e=new Set){if(e.has(n))return;if(e.add(n),o(n))return n;const t=["children","items","panes","tabs","child","ui"];for(const r of t){const t=n[r];if(t){const n=Array.isArray(t)?t:[t];for(const t of n)if(t instanceof i.Model&&!e.has(t)){const n=c(t,o,e);if(n)return n}}}}function u(n){var o;const e=null===(o=null==n?void 0:n.document)||void 0===o?void 0:o.all_roots;if(e){const o=e.flatMap((n=>{const o=n;return o&&"cubevis.bokeh.models._bokeh_app_context.BokehAppContext"===o.type?[o]:[]}));return o.find((o=>Boolean(c(o,(o=>o.id===n.id)))))}}},
|
|
56
56
|
"9d3c34ff8e": function _(e,o,l,s,t){var c;s();const _=e("@bokehjs/models/tools/actions/reset_tool"),i=e("@bokehjs/styles/icons.css"),a=e("@bokehjs/core/util/callbacks");class n extends _.ResetToolView{doit(){const{precallback:e,postcallback:o}=this.model;null!=e&&(0,a.execute)(e,this.model),this.plot_view.reset(),null!=o&&(0,a.execute)(o,this.model)}}l.CBResetToolView=n,n.__name__="CBResetToolView";class r extends _.ResetTool{constructor(e){super(e),this.tool_name="CBReset",this.tool_icon=i.tool_icon_reset}}l.CBResetTool=r,c=r,r.__name__="CBResetTool",r.__module__="cubevis.bokeh.tools._cbreset_tool",c.prototype.default_view=n,c.define((({Any:e,Nullable:o})=>({precallback:[o(e),null],postcallback:[o(e),null]}))),c.register_alias("cbreset",(()=>new c))},
|
|
57
57
|
"9f961622ce": function _(e,t,i,o,s){var n;o();const l=e("@bokehjs/models/widgets/abstract_button"),c=e("@bokehjs/models/ui/tooltip"),d=e("@bokehjs/models/ui/icons/builtin_icon"),u=e("@bokehjs/core/build_views"),h=e("@bokehjs/core/bokeh_events"),r=e("@bokehjs/core/util/object");class a extends l.AbstractButtonView{constructor(){super(...arguments),this.isMouseInside=!1}click(){this.model.trigger_event(new h.ButtonClick),super.click()}*children(){yield*super.children(),yield this.tooltip}Show(){this.tooltip.model.setv({visible:!0,closable:!1})}unShow(){this.tooltip.model.setv({visible:!1,closable:!1})}async lazy_initialize(){const{hover_wait:e}=this.model;this.debouncedShow=casalib.debounce((()=>{this.isMouseInside&&this.Show()}),1e3*e),await super.lazy_initialize();const{tooltip:t}=this.model;this.tooltip=await(0,u.build_view)(t,{parent:this})}remove(){this.tooltip.remove(),super.remove()}render(){super.render(),this.el.addEventListener("mouseenter",(()=>{this.isMouseInside=!0,this.debouncedShow()})),this.el.addEventListener("mouseleave",(()=>{this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),document.addEventListener("mousedown",(e=>{if(void 0===(0,r.dict)(this.model.js_event_callbacks).get(h.ButtonClick.prototype.event_name)){if(e.composedPath().includes(this.tooltip.el))return;this.debouncedShow.cancel(),this.unShow()}else this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),window.addEventListener("blur",(()=>{this.debouncedShow.cancel(),this.unShow()}))}}i.TipButtonView=a,a.__name__="TipButtonView";class b extends l.AbstractButton{constructor(e){super(e)}on_click(e){this.on_event(h.ButtonClick,e)}}i.TipButton=b,n=b,b.__name__="TipButton",b.__module__="cubevis.bokeh.models._tip_button",n.prototype.default_view=a,n.define((({Ref:e,Number:t})=>({tooltip:[e(c.Tooltip)],hover_wait:[t,1.5]}))),n.override({label:"",icon:new d.BuiltinIcon({icon_name:"help",size:18}),button_type:"default"})},
|
|
58
58
|
"ca4c845905": function _(e,i,s,t,o){var l;t();const d=e("@bokehjs/models/layouts/layout_dom"),n=e("@bokehjs/models/ui/ui_element"),h=e("@bokehjs/models/ui/tooltip"),r=e("@bokehjs/core/dom"),a=e("@bokehjs/core/build_views");class c extends d.LayoutDOMView{constructor(){super(...arguments),this.isMouseInside=!1}stylesheets(){return[...super.stylesheets(),"*, *:before, *:after { box-sizing: border-box; } fieldset { border: 0px; margin: 0px; padding: 0px; }"]}Show(){this.tooltip.model.setv({visible:!0,closable:!1})}unShow(){this.tooltip.model.setv({visible:!1,closable:!1})}async lazy_initialize(){const{hover_wait:e}=this.model;this.debouncedShow=casalib.debounce((()=>{this.isMouseInside&&this.Show()}),1e3*e),await super.lazy_initialize(),await this.build_child_views();const{tooltip:i}=this.model;this.tooltip=await(0,a.build_view)(i,{parent:this})}remove(){this.tooltip.remove(),super.remove()}connect_signals(){super.connect_signals();const{child:e}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.el.addEventListener("mouseenter",(e=>{this.mouseenter.emit(e)})),this.el.addEventListener("mouseleave",(e=>{this.mouseleave.emit(e)}))}*children(){yield*super.children(),yield this.tooltip}get child_models(){return[this.model.child]}render(){super.render(),this.el.addEventListener("mouseenter",(()=>{this.isMouseInside=!0,this.debouncedShow()})),this.el.addEventListener("mouseleave",(()=>{this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),window.addEventListener("blur",(()=>{this.debouncedShow.cancel(),this.unShow()})),this.el.addEventListener("click",(()=>{this.debouncedShow.cancel(),this.unShow()}));const e=this.child_views.map((e=>e.el));this.fieldset_el=(0,r.fieldset)({},...e),this.shadow_el.appendChild(this.fieldset_el)}_update_children(){const e=this.child_views.map((e=>e.el));this.fieldset_el.append(...e)}}s.TipView=c,c.__name__="TipView";class u extends d.LayoutDOM{constructor(e){super(e)}}s.Tip=u,l=u,u.__name__="Tip",u.__module__="cubevis.bokeh.models._tip",l.prototype.default_view=c,l.define((({Ref:e,Number:i})=>({child:[e(n.UIElement)],tooltip:[e(h.Tooltip)],hover_wait:[i,1.5]})))},
|
|
59
59
|
"5e5f767c53": function _(e,i,t,s,l){var a;s();const o=e("@bokehjs/models/layouts/layout_dom"),n=e("@bokehjs/models/ui/ui_element");class u extends o.LayoutDOMView{get child_models(){return null!=this.model.ui?[this.model.ui]:[]}initialize(){super.initialize(),this.el.setAttribute("data-lm-suppress-shortcuts","true")}async lazy_initialize(){await super.lazy_initialize()}connect_signals(){super.connect_signals()}_update_layout(){super._update_layout()}render(){super.render(),0===this.child_views.length&&null==this.model.ui&&(this.el.innerHTML='<div style="color: gray; padding: 10px; border: 1px dashed gray;">\n Showable: No UI element set\n </div>')}after_layout(){super.after_layout()}_intrinsic_display(){return super._intrinsic_display()}remove(){super.remove()}}t.ShowableView=u,u.__name__="ShowableView";class r extends o.LayoutDOM{constructor(e){super(e)}}t.Showable=r,a=r,r.__name__="Showable",r.__module__="cubevis.bokeh.models._showable",a.prototype.default_view=u,a.define((({Ref:e})=>({ui:[e(n.UIElement)]})))},
|
|
60
|
+
"467d2716b0": function _(e,i,s,t,o){var n;t();const p=e("@bokehjs/models/layouts/layout_dom"),a=e("@bokehjs/models/ui/ui_element");class l extends p.LayoutDOMView{get child_models(){return null!=this.model.ui?[this.model.ui]:[]}initialize(){super.initialize(),this.el&&(this.el.style.padding="0",this.el.style.margin="0",this.el.style.border="none",this.el.style.background="transparent",this.el.style.display="contents"),this.connect(this.model.properties.app_state.change,(()=>{const e=window.cubevisAppSession;(null==e?void 0:e.applications[this.model.app_id])&&(e.applications[this.model.app_id].state=this.model.app_state)}))}}s.BokehAppContextView=l,l.__name__="BokehAppContextView";class d extends p.LayoutDOM{constructor(e){super(e)}initialize(){super.initialize(),window.cubevisAppSession||(window.cubevisAppSession={sessionId:this.session_id,applications:{}},console.log(`Initialized Bokeh session: ${this.session_id}`));const e=window.cubevisAppSession;e.applications[this.app_id]||(e.applications[this.app_id]={appId:this.app_id,state:this.app_state,createdAt:(new Date).toISOString()},console.log(`Registered application: ${this.app_id}`))}}s.BokehAppContext=d,n=d,d.__name__="BokehAppContext",d.__module__="cubevis.bokeh.models._bokeh_app_context",n.prototype.default_view=l,n.define((({Ref:e,Nullable:i,Dict:s,String:t,Unknown:o})=>({ui:[i(e(a.UIElement)),null],app_id:[t,""],session_id:[t,""],app_state:[s(o),{}]})))},
|
|
60
61
|
"50a1e32f01": function _(e,s,i,n,t){var a;n();const c=e("@bokehjs/core/view"),o=e("@bokehjs/model");class _ extends c.View{initialize(){super.initialize()}connect_signals(){super.connect_signals();const{values:e}=this.model.properties;this.on_change(e,(()=>this.values_changed()))}values_changed(){}}i.SharedDictView=_,_.__name__="SharedDictView";class d extends o.Model{constructor(e){super(e)}}i.SharedDict=d,a=d,d.__name__="SharedDict",d.__module__="cubevis.bokeh.models._shared_dict",a.prototype.default_view=_,a.define((({Dict:e,Unknown:s})=>({values:[e(s),{}]})))},
|
|
61
62
|
"b55081402e": function _(e,n,t,_,s){var a;_();const o=e("@bokehjs/models/annotations/span"),p=e("@bokehjs/core/bokeh_events");class r extends o.SpanView{on_pan_start(e){const n=super.on_pan_start(e);return this.model.trigger_event(new p.LODStart),n}on_pan(e){super.on_pan(e)}on_pan_end(e){super.on_pan_end(e),this.model.trigger_event(new p.LODEnd)}}t.EditSpanView=r,r.__name__="EditSpanView";class d extends o.Span{constructor(e){super(e)}}t.EditSpan=d,a=d,d.__name__="EditSpan",d.__module__="cubevis.bokeh.models._edit_span",a.prototype.default_view=r},
|
|
62
63
|
"9144bfc7a5": function _(e,t,s,n,r){var i;n();const l=e("@bokehjs/models/widgets/text_input"),o=e("@bokehjs/core/dom"),u=e("@bokehjs/core/bokeh_events");class _ extends l.TextInputView{stylesheets(){return[...super.stylesheets(),new o.InlineStyleSheet(".bk-input-prefix { padding: 0 var(--padding-vertical); }")]}connect_signals(){super.connect_signals(),this.el.addEventListener("mouseenter",(e=>{this.model.trigger_event(new u.MouseEnter(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))})),this.el.addEventListener("mouseleave",(e=>{this.model.trigger_event(new u.MouseLeave(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))}))}render(){super.render()}}s.EvTextInputView=_,_.__name__="EvTextInputView";class c extends l.TextInput{constructor(e){super(e)}}s.EvTextInput=c,i=c,c.__name__="EvTextInput",c.__module__="cubevis.bokeh.models._ev_text_input",i.prototype.default_view=_},
|
|
63
64
|
"74e0abef8a": function _(e,t,n,s,o){var i;s();const r=e("@bokehjs/models/annotations/poly_annotation"),a=e("@bokehjs/core/bokeh_events");class _ extends r.PolyAnnotationView{on_enter(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseEnter(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_enter(e);return this.model.trigger_event(s),o}on_leave(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseLeave(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_leave(e),this.model.trigger_event(s)}on_pan_start(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanStart(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_pan_start(e);return this.model.trigger_event(s),o}on_pan_end(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanEnd(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_pan_end(e),this.model.trigger_event(s)}on_pan(e){super.on_pan(e);const t=new a.RangesUpdate(e.sx,e.sx+e.dx,e.sy,e.sy+e.dy);this.model.trigger_event(t)}}n.EvPolyAnnotationView=_,_.__name__="EvPolyAnnotationView";class l extends r.PolyAnnotation{constructor(e){super(e)}}n.EvPolyAnnotation=l,i=l,l.__name__="EvPolyAnnotation",l.__module__="cubevis.bokeh.annotations._ev_poly_annotation",i.prototype.default_view=_},
|
|
64
|
-
}, "
|
|
65
|
+
}, "e2864cf297", {"index":"e2864cf297","src/bokeh/sources/data_pipe":"87aeec7876","src/bokeh/util/conversions":"e3901fa9f2","src/bokeh/sources/image_pipe":"2889e0dd45","src/bokeh/sources/image_data_source":"de65005924","src/bokeh/sources/spectra_data_source":"02e3c3e46c","src/bokeh/sources/updatable_data_source":"64b16deff9","src/bokeh/format/wcs_ticks":"b6ae454f0d","src/bokeh/tools/drag_tool":"cb7d28d6b3","src/bokeh/events":"8fc7a9e935","src/bokeh/util/find":"389c724eb9","src/bokeh/tools/cbreset_tool":"9d3c34ff8e","src/bokeh/models/tip_button":"9f961622ce","src/bokeh/models/tip":"ca4c845905","src/bokeh/models/showable":"5e5f767c53","src/bokeh/models/bokeh_app_context":"467d2716b0","src/bokeh/models/shared_dict":"50a1e32f01","src/bokeh/models/edit_span":"b55081402e","src/bokeh/models/ev_text_input":"9144bfc7a5","src/bokeh/annotations/ev_poly_annotation":"74e0abef8a"}, {});});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from bokeh.core.properties import String, Dict, Any, Nullable, Instance
|
|
3
|
+
from bokeh.models.layouts import LayoutDOM
|
|
4
|
+
from bokeh.models.ui import UIElement
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
class BokehAppContext(LayoutDOM):
|
|
10
|
+
"""
|
|
11
|
+
Custom Bokeh model that bridges Python AppContext with JavaScript.
|
|
12
|
+
Initializes session-level data structure and app-specific state.
|
|
13
|
+
"""
|
|
14
|
+
ui = Nullable(Instance(UIElement), help="""
|
|
15
|
+
A UI element, which can be plots, layouts, widgets, or any other UIElement.
|
|
16
|
+
""")
|
|
17
|
+
|
|
18
|
+
app_id = String(default="")
|
|
19
|
+
session_id = String(default="")
|
|
20
|
+
app_state = Dict(String, Any, default={})
|
|
21
|
+
|
|
22
|
+
# Class-level session ID shared across all apps in the same Python session
|
|
23
|
+
_session_id = None
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def get_session_id(cls):
|
|
27
|
+
"""Get or create a session ID for this Python session"""
|
|
28
|
+
if cls._session_id is None:
|
|
29
|
+
cls._session_id = str(uuid4())
|
|
30
|
+
return cls._session_id
|
|
31
|
+
|
|
32
|
+
def __init__( self, ui=None, **kwargs ):
|
|
33
|
+
logger.debug(f"\tBokehAppContext::__init__(ui={type(ui).__name__ if ui else None}, {kwargs}): {id(self)}")
|
|
34
|
+
|
|
35
|
+
if ui is not None and 'ui' in kwargs:
|
|
36
|
+
raise RuntimeError( "'ui' supplied as both a positional parameter and a keyword parameter" )
|
|
37
|
+
|
|
38
|
+
kwargs['session_id'] = self.get_session_id( )
|
|
39
|
+
|
|
40
|
+
if 'ui' not in kwargs:
|
|
41
|
+
kwargs['ui'] = ui
|
|
42
|
+
if 'app_id' not in kwargs:
|
|
43
|
+
kwargs['app_id'] = str(uuid4())
|
|
44
|
+
|
|
45
|
+
super().__init__(**kwargs)
|
|
46
|
+
|
|
47
|
+
def _sphinx_height_hint(self):
|
|
48
|
+
"""Delegate height hint to the wrapped UI element"""
|
|
49
|
+
logger.debug(f"\tShowable::_sphinx_height_hint(): {id(self)}")
|
|
50
|
+
if self.ui and hasattr(self.ui, '_sphinx_height_hint'):
|
|
51
|
+
return self.ui._sphinx_height_hint()
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def update_app_state(self, state_updates):
|
|
55
|
+
"""
|
|
56
|
+
Update the application state (will be in the generated HTML/JS)
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
state_updates: dict of state key-value pairs to update
|
|
60
|
+
"""
|
|
61
|
+
current_state = dict(self.app_state)
|
|
62
|
+
current_state.update(state_updates)
|
|
63
|
+
self.app_state = current_state
|
|
@@ -36,6 +36,7 @@ import asyncio
|
|
|
36
36
|
import traceback
|
|
37
37
|
import time
|
|
38
38
|
import json
|
|
39
|
+
from uuid import uuid4
|
|
39
40
|
|
|
40
41
|
from bokeh.models.sources import DataSource
|
|
41
42
|
from bokeh.util.compiler import TypeScript
|
|
@@ -72,7 +73,9 @@ class DataPipe(DataSource,BokehInit):
|
|
|
72
73
|
|
|
73
74
|
address = Tuple( String, Int, help="two integer sequence representing the address and port to use for the websocket" )
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
instance_id = String( help="Unique ID for each DataPipe object" )
|
|
77
|
+
|
|
78
|
+
conflict_check = Bool( default=True, help="Perform check to avoid reuse of URL for GUI. Not needed in the Jupyter context" )
|
|
76
79
|
|
|
77
80
|
# Class-level session tracking to prevent multiple connections
|
|
78
81
|
_active_sessions = {} # session_id -> {'websocket': ws, 'timestamp': time, 'datapipe': instance}
|
|
@@ -87,8 +90,11 @@ class DataPipe(DataSource,BokehInit):
|
|
|
87
90
|
|
|
88
91
|
if 'conflict_check' not in kwargs:
|
|
89
92
|
kwargs['conflict_check'] = not is_interactive_jupyter( )
|
|
93
|
+
if 'instance_id' not in kwargs:
|
|
94
|
+
kwargs['instance_id'] = str(uuid4( ))
|
|
90
95
|
|
|
91
96
|
super( ).__init__( *args, **kwargs )
|
|
97
|
+
|
|
92
98
|
self.__send_queue = { }
|
|
93
99
|
self.__pending = { }
|
|
94
100
|
self.__incoming_callbacks = { }
|
|
@@ -43,7 +43,7 @@ from bokeh.models.dom import HTML
|
|
|
43
43
|
from bokeh.models.ui.tooltips import Tooltip
|
|
44
44
|
from cubevis.utils import resource_manager, reset_resource_manager, is_interactive_jupyter
|
|
45
45
|
from cubevis.data import casaimage
|
|
46
|
-
from cubevis.bokeh.models import TipButton, Tip
|
|
46
|
+
from cubevis.bokeh.models import TipButton, Tip, BokehAppContext
|
|
47
47
|
from cubevis.utils import ContextMgrChain as CMC
|
|
48
48
|
|
|
49
49
|
class CreateMask:
|
|
@@ -243,23 +243,7 @@ class CreateMask:
|
|
|
243
243
|
### If debugging this, make only a small change before confirming that exit from the
|
|
244
244
|
### Python asyncio loop continues to work... seems to be fiddly
|
|
245
245
|
###
|
|
246
|
-
imdetails['gui']['cube'] = CubeMask( paths[0], mask=paths[1]
|
|
247
|
-
init_script = None if initialization_registered else \
|
|
248
|
-
CustomJS( args={ }, code='''
|
|
249
|
-
window.addEventListener( 'beforeunload',
|
|
250
|
-
(event) => {
|
|
251
|
-
function donePromise( ) {
|
|
252
|
-
|
|
253
|
-
return new Promise( (resolve,reject) => {
|
|
254
|
-
// call by name does not work here:
|
|
255
|
-
// document._cube_done(cb=resolve) ???
|
|
256
|
-
if ( document._cube_done ) document._cube_done(null,resolve)
|
|
257
|
-
} )
|
|
258
|
-
}
|
|
259
|
-
( async () => { await donePromise( ) } )( )
|
|
260
|
-
} )''' ) )
|
|
261
|
-
|
|
262
|
-
initialization_registered = True
|
|
246
|
+
imdetails['gui']['cube'] = CubeMask( paths[0], mask=paths[1] )
|
|
263
247
|
imdetails['image-channels'] = imdetails['gui']['cube'].shape( )[3]
|
|
264
248
|
|
|
265
249
|
|
|
@@ -276,6 +260,23 @@ class CreateMask:
|
|
|
276
260
|
self._fig['status'] = imdetails['gui']['status'] = imdetails['gui']['cube'].status_text( "<p>initialization</p>" , width=230, reuse=self._fig['status'] )
|
|
277
261
|
self._image_bitmask_controls = imdetails['gui']['cube'].bitmask_ctrl( reuse=self._image_bitmask_controls, button_type='light' )
|
|
278
262
|
|
|
263
|
+
|
|
264
|
+
,
|
|
265
|
+
imdetails['gui']['cube'].init_script = None if initialization_registered else \
|
|
266
|
+
CustomJS( args={ 'status': self._fig['status'] },
|
|
267
|
+
### app state is found based on one of the GUI's models
|
|
268
|
+
code='''const appstate = Bokeh.find.appState(status)
|
|
269
|
+
window.addEventListener( 'beforeunload',
|
|
270
|
+
(event) => {
|
|
271
|
+
function donePromise( ) {
|
|
272
|
+
return new Promise( (resolve,reject) => {
|
|
273
|
+
appstate?.cube_done?.(null,resolve)
|
|
274
|
+
} )
|
|
275
|
+
}
|
|
276
|
+
( async () => { await donePromise( ) } )( )
|
|
277
|
+
} )''' )
|
|
278
|
+
initialization_registered = True
|
|
279
|
+
|
|
279
280
|
###
|
|
280
281
|
### spectrum plot must be disabled during iteration due to "tap to change channel" functionality
|
|
281
282
|
###
|
|
@@ -319,7 +320,8 @@ class CreateMask:
|
|
|
319
320
|
|
|
320
321
|
self._image_control_tab_groups[imid] = result
|
|
321
322
|
result.js_on_change( 'active', CustomJS( args={ },
|
|
322
|
-
code='''
|
|
323
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
324
|
+
appstate.last_control_tab = cb_obj.active''' ) )
|
|
323
325
|
return result
|
|
324
326
|
|
|
325
327
|
def _create_image_panel( self, imagetuple ):
|
|
@@ -356,7 +358,8 @@ class CreateMask:
|
|
|
356
358
|
|
|
357
359
|
self._ctrl_state['stop'].js_on_click( CustomJS( args={ },
|
|
358
360
|
code='''if ( confirm( "Are you sure you want to end this mask creation session and close the GUI?" ) ) {
|
|
359
|
-
|
|
361
|
+
const appstate = Bokeh.find.appState(cb_obj)
|
|
362
|
+
appstate?.cube_done?.( )
|
|
360
363
|
}''' ) )
|
|
361
364
|
|
|
362
365
|
|
|
@@ -368,17 +371,21 @@ class CreateMask:
|
|
|
368
371
|
|
|
369
372
|
image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
|
|
370
373
|
|
|
371
|
-
self._fig['layout'] =
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
self._fig['layout'] = BokehAppContext(
|
|
375
|
+
column( row( self._fig['help'],
|
|
376
|
+
Spacer( height=self._ctrl_state['stop'].height, sizing_mode="scale_width" ),
|
|
377
|
+
Div( text="<div><b>status:</b></div>" ),
|
|
378
|
+
self._fig['status'],
|
|
379
|
+
self._ctrl_state['stop'], sizing_mode="scale_width" ),
|
|
380
|
+
row( image_tabs, height_policy='max', width_policy='max' ),
|
|
381
|
+
height_policy='max', width_policy='max' ),
|
|
382
|
+
app_state={ ### while the state dictionary itself
|
|
383
|
+
'name': 'create mask', ### is used, these particular element
|
|
384
|
+
'initialized': True ### are not currently used for anything
|
|
385
|
+
} )
|
|
379
386
|
|
|
380
387
|
###
|
|
381
|
-
### Keep track of which image is currently active in
|
|
388
|
+
### Keep track of which image is currently active in appstate.image_name (which is
|
|
382
389
|
### initialized in self._js['initialize']). Also, update the current control sub-tab
|
|
383
390
|
### when the field main-tab is changed. An attempt to manage this all within the
|
|
384
391
|
### control sub-tabs using a reference to self._image_control_tab_groups from
|
|
@@ -388,11 +395,12 @@ class CreateMask:
|
|
|
388
395
|
###
|
|
389
396
|
image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._mask_state.items( ) ],
|
|
390
397
|
itergroups=self._image_control_tab_groups ),
|
|
391
|
-
code='''
|
|
392
|
-
|
|
398
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
399
|
+
if ( ! hasprop(appstate,'last_control_tab') ) {
|
|
400
|
+
appstate.last_control_tab = 0
|
|
393
401
|
}
|
|
394
|
-
|
|
395
|
-
itergroups[
|
|
402
|
+
appstate.image_name = names[cb_obj.active]
|
|
403
|
+
itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
|
|
396
404
|
|
|
397
405
|
# Change display type depending on runtime environment
|
|
398
406
|
if is_interactive_jupyter( ):
|
|
@@ -43,7 +43,7 @@ from bokeh.models.dom import HTML
|
|
|
43
43
|
from bokeh.models.ui.tooltips import Tooltip
|
|
44
44
|
from cubevis.utils import resource_manager, reset_resource_manager, is_interactive_jupyter
|
|
45
45
|
from cubevis.data import casaimage
|
|
46
|
-
from cubevis.bokeh.models import TipButton, Tip
|
|
46
|
+
from cubevis.bokeh.models import TipButton, Tip, BokehAppContext
|
|
47
47
|
from cubevis.utils import ContextMgrChain as CMC
|
|
48
48
|
|
|
49
49
|
class CreateRegion:
|
|
@@ -190,7 +190,7 @@ class CreateRegion:
|
|
|
190
190
|
|
|
191
191
|
###
|
|
192
192
|
### Use CubeMask init_script to set up a 'beforeunload' handler which will signal to
|
|
193
|
-
### CubeMask that the app is shuting down (with '
|
|
193
|
+
### CubeMask that the app is shuting down (with 'appstate.cube_done( )'). It should then
|
|
194
194
|
### send the final results to Python and then call the provided callback (the Promise
|
|
195
195
|
### resolve function).
|
|
196
196
|
###
|
|
@@ -231,7 +231,8 @@ class CreateRegion:
|
|
|
231
231
|
imdetails['gui']['slider'] = None
|
|
232
232
|
imdetails['gui']['goto'] = None
|
|
233
233
|
|
|
234
|
-
init_args = { '
|
|
234
|
+
init_args = { 'status': self._fig['status'],
|
|
235
|
+
'sources': { } }
|
|
235
236
|
last_cube = None
|
|
236
237
|
for k,v in self._region_state.items( ):
|
|
237
238
|
init_args['sources'][k] = v['gui']['image']['src']
|
|
@@ -246,8 +247,9 @@ class CreateRegion:
|
|
|
246
247
|
acc[src.id] = img
|
|
247
248
|
if ( source == null ) source = src
|
|
248
249
|
return acc }}, sources, {{ }} )
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
const appstate = Bokeh.find.appState(status)
|
|
251
|
+
if ( source && appstate?.cube_done )
|
|
252
|
+
appstate.cube_done(
|
|
251
253
|
casalib.reduce(
|
|
252
254
|
(acc, poly) => {{
|
|
253
255
|
acc[srcmap[poly.source.id]][poly.label] = {{
|
|
@@ -265,8 +267,8 @@ class CreateRegion:
|
|
|
265
267
|
## ImageDataSources (which is an element of CubeMask). This resulted in a circular
|
|
266
268
|
## reference when the plot was being rendered.
|
|
267
269
|
##
|
|
268
|
-
## The
|
|
269
|
-
## return `false` (indicating that the window should not be closed by
|
|
270
|
+
## The appstate.cube_done callback ( `(msg) => { resolve(true); return false }` ) can
|
|
271
|
+
## return `false` (indicating that the window should not be closed by cube_done)
|
|
270
272
|
## because the window is already being closed when this beforeunload callback
|
|
271
273
|
## is called.
|
|
272
274
|
##
|
|
@@ -385,7 +387,8 @@ class CreateRegion:
|
|
|
385
387
|
|
|
386
388
|
self._image_control_tab_groups[imid] = result
|
|
387
389
|
result.js_on_change( 'active', CustomJS( args=dict( ),
|
|
388
|
-
code='''
|
|
390
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
391
|
+
appstate.last_control_tab = cb_obj.active''' ) )
|
|
389
392
|
return result
|
|
390
393
|
|
|
391
394
|
def _create_image_panel( self, imagetuple ):
|
|
@@ -421,17 +424,21 @@ class CreateRegion:
|
|
|
421
424
|
|
|
422
425
|
image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
|
|
423
426
|
|
|
424
|
-
self._fig['layout'] =
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
427
|
+
self._fig['layout'] = BokehAppContext(
|
|
428
|
+
column( row( self._fig['help'],
|
|
429
|
+
Spacer( height=self._ctrl_state['stop'].height, sizing_mode="scale_width" ),
|
|
430
|
+
Div( text="<div><b>status:</b></div>" ),
|
|
431
|
+
self._fig['status'],
|
|
432
|
+
self._ctrl_state['stop'], sizing_mode="scale_width" ),
|
|
433
|
+
row( image_tabs, height_policy='max', width_policy='max' ),
|
|
434
|
+
height_policy='max', width_policy='max' ),
|
|
435
|
+
app_state={ ### while the state dictionary itself
|
|
436
|
+
'name': 'create region', ### is used, these particular element
|
|
437
|
+
'initialized': True ### are not currently used for anything
|
|
438
|
+
} )
|
|
432
439
|
|
|
433
440
|
###
|
|
434
|
-
### Keep track of which image is currently active in
|
|
441
|
+
### Keep track of which image is currently active in appstate.image_name (which is
|
|
435
442
|
### initialized in self._js['initialize']). Also, update the current control sub-tab
|
|
436
443
|
### when the field main-tab is changed. An attempt to manage this all within the
|
|
437
444
|
### control sub-tabs using a reference to self._image_control_tab_groups from
|
|
@@ -441,11 +448,12 @@ class CreateRegion:
|
|
|
441
448
|
###
|
|
442
449
|
image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._region_state.items( ) ],
|
|
443
450
|
itergroups=self._image_control_tab_groups ),
|
|
444
|
-
code='''
|
|
445
|
-
|
|
451
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
452
|
+
if ( ! hasprop(appstate,'last_control_tab') ) {
|
|
453
|
+
appstate.last_control_tab = 0
|
|
446
454
|
}
|
|
447
|
-
|
|
448
|
-
itergroups[
|
|
455
|
+
appstate.image_name = names[cb_obj.active]
|
|
456
|
+
itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
|
|
449
457
|
|
|
450
458
|
# Change display type depending on runtime environment
|
|
451
459
|
if is_interactive_jupyter( ):
|
|
@@ -1488,20 +1488,23 @@ class CubeMask:
|
|
|
1488
1488
|
selector=self._bitmask_color_selector,
|
|
1489
1489
|
disable_add_sub = self._mask_add_sub_disable,
|
|
1490
1490
|
user_init_script = self.init_script,
|
|
1491
|
-
freeze_cb = self._image_freeze_cb
|
|
1491
|
+
freeze_cb = self._image_freeze_cb,
|
|
1492
|
+
image=self._image )
|
|
1492
1493
|
|
|
1493
1494
|
self._image_source.init_script = CustomJS( args=init_args,
|
|
1494
1495
|
code='''let source = cb_obj
|
|
1495
|
-
|
|
1496
|
-
|
|
1496
|
+
const appstate = Bokeh.find.appState(image)
|
|
1497
|
+
appstate.cube_already_shutdown = false
|
|
1498
|
+
''' + self._js['mask-state-init'] +
|
|
1497
1499
|
( self._js['func-curmasks']( ) +
|
|
1498
1500
|
self._js['key-state-funcs']
|
|
1499
1501
|
if self._mask_path is None else '' ) +
|
|
1500
1502
|
self._js['update-status'] + self._js['setup-key-mgmt'] +
|
|
1501
1503
|
"""// This function is called to collect the masks and/or stop
|
|
1502
1504
|
// -->> collect_masks( ) is only defined if bitmask cube is NOT used
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
+
console.log( "App state:", appstate )
|
|
1506
|
+
appstate.cube_done = ( final_polys=null, cb=null ) => {
|
|
1507
|
+
if ( appstate.cube_already_shutdown ) return
|
|
1505
1508
|
function done_close_window( msg ) {
|
|
1506
1509
|
if ( msg.result === 'stopped' ) {""" +
|
|
1507
1510
|
# Don't close tab if running in a jupyter notebook
|
|
@@ -1516,7 +1519,7 @@ class CubeMask:
|
|
|
1516
1519
|
"""
|
|
1517
1520
|
}
|
|
1518
1521
|
}
|
|
1519
|
-
|
|
1522
|
+
appstate.cube_already_shutdown = true
|
|
1520
1523
|
ctrl.send( ids['done'],
|
|
1521
1524
|
{ action: 'done',
|
|
1522
1525
|
value: { regions: final_polys ? final_polys : source.masks( ) },
|
|
@@ -1526,13 +1529,12 @@ class CubeMask:
|
|
|
1526
1529
|
// exported functions -- enable/disable masking, retrieve masks etc.
|
|
1527
1530
|
source._masking_enabled = true
|
|
1528
1531
|
source._pixel_update_enabled = true
|
|
1529
|
-
if ( ! ('
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
source.
|
|
1534
|
-
source.
|
|
1535
|
-
source.enable_masking = ( ) => { source._masking_enabled = true; document._masking_state[source.id] = true }
|
|
1532
|
+
if ( ! ('masking_state' in appstate) )
|
|
1533
|
+
appstate.masking_state = { }
|
|
1534
|
+
appstate.masking_state[source.id] = false
|
|
1535
|
+
source.masking_on = ( ) => { return appstate.masking_state[source.id] }
|
|
1536
|
+
source.disable_masking = ( ) => { source._masking_enabled = false; appstate.masking_state[source.id] = false }
|
|
1537
|
+
source.enable_masking = ( ) => { source._masking_enabled = true; appstate.masking_state[source.id] = true }
|
|
1536
1538
|
source.disable_pixel_update = ( ) => source._pixel_update_enabled = false
|
|
1537
1539
|
source.enable_pixel_update = ( ) => source._pixel_update_enabled = true
|
|
1538
1540
|
source.masks = ( ) => typeof collect_masks == 'function' ? collect_masks( ) : { masks: [], polys: [] }
|
|
@@ -1549,7 +1551,7 @@ class CubeMask:
|
|
|
1549
1551
|
if ( stats_source ) source.update_statistics( stats_source.data ) /*** round pre-filled floats ***/
|
|
1550
1552
|
/*** this is the hook that allows the user to disable mask changes ***/
|
|
1551
1553
|
this.disable_add_sub = disable_add_sub
|
|
1552
|
-
if ( user_init_script ) { user_init_script.execute(this) }
|
|
1554
|
+
if ( user_init_script ) { user_init_script.execute({ origin: this, appstate }) }
|
|
1553
1555
|
""" )
|
|
1554
1556
|
|
|
1555
1557
|
def region_position_connections( ):
|
|
@@ -2189,7 +2191,7 @@ class CubeMask:
|
|
|
2189
2191
|
stokes_labels=[ k for k,_ in self._region_controls['coord']['chan'].items( ) ],
|
|
2190
2192
|
status_line=self._region_controls['coord']['status'] if self._mask_path is None else None,
|
|
2191
2193
|
chan_select = [ v.child for k,v in self._region_controls['coord']['chan'].items( ) ] ),
|
|
2192
|
-
code= ( self._js['func-newpoly'] + self._js['func-curmasks']( ) +
|
|
2194
|
+
code= ( "const appstate = Bokeh.find.appState(cb_obj.origin);" + self._js['func-newpoly'] + self._js['func-curmasks']( ) +
|
|
2193
2195
|
self._js['mask-state-init'] + self._js_mode_code['no-bitmask-tool-selection'] )
|
|
2194
2196
|
if self._mask_path is None else "" + (
|
|
2195
2197
|
### selector indicates if a on-disk mask is being used
|
|
@@ -2655,10 +2657,10 @@ class CubeMask:
|
|
|
2655
2657
|
'no-bitmask-init': '''function _create_poly_mgr( annos, stokes_labels ) {
|
|
2656
2658
|
const chan2polys = { }
|
|
2657
2659
|
|
|
2658
|
-
if ( typeof
|
|
2659
|
-
|
|
2660
|
+
if ( typeof appstate.polygon_list == 'undefined' )
|
|
2661
|
+
appstate.polygon_list = [ ]
|
|
2660
2662
|
|
|
2661
|
-
const polys =
|
|
2663
|
+
const polys = appstate.polygon_list
|
|
2662
2664
|
|
|
2663
2665
|
return {
|
|
2664
2666
|
reset_annos: ( ) => {
|