cubevis 1.0.5__py3-none-any.whl → 1.0.21__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/__js__/bokeh-3.6/cubevisjs.min.js +13 -10
- cubevis/__js__/bokeh-3.7/cubevisjs.min.js +12 -8
- cubevis/__js__/bokeh-3.8/cubevisjs.min.js +12 -9
- cubevis/__version__.py +1 -1
- cubevis/bokeh/models/__init__.py +1 -0
- cubevis/bokeh/models/_bokeh_app_context.py +110 -0
- cubevis/bokeh/models/_showable.py +108 -82
- cubevis/bokeh/sources/_data_pipe.py +14 -2
- cubevis/exe/_task.py +36 -16
- cubevis/private/apps/_createmask.py +43 -41
- cubevis/private/apps/_createregion.py +31 -29
- cubevis/private/apps/_interactiveclean.mustache +2 -1
- cubevis/private/apps/_interactiveclean.py +2 -1
- cubevis/private/apps/_interactivecleannotebook.mustache +24 -1
- cubevis/private/apps/_interactivecleannotebook.py +24 -1
- cubevis/toolbox/__init__.py +0 -1
- cubevis/toolbox/_cube.py +40 -23
- cubevis/toolbox/_interactive_clean_ui.mustache +82 -50
- cubevis/toolbox/_interactive_clean_ui.py +82 -50
- cubevis/utils/__init__.py +1 -0
- cubevis/utils/_mutual_exclusion.py +117 -0
- {cubevis-1.0.5.dist-info → cubevis-1.0.21.dist-info}/METADATA +2 -2
- {cubevis-1.0.5.dist-info → cubevis-1.0.21.dist-info}/RECORD +25 -23
- {cubevis-1.0.5.dist-info → cubevis-1.0.21.dist-info}/WHEEL +0 -0
- {cubevis-1.0.5.dist-info → cubevis-1.0.21.dist-info}/licenses/LICENSE +0 -0
|
@@ -35,7 +35,7 @@ from contextlib import asynccontextmanager
|
|
|
35
35
|
from bokeh.layouts import row, column
|
|
36
36
|
from bokeh.plotting import show
|
|
37
37
|
from bokeh.models import Button, CustomJS, TabPanel, Tabs, Spacer, Div
|
|
38
|
-
from cubevis.toolbox import CubeMask
|
|
38
|
+
from cubevis.toolbox import CubeMask
|
|
39
39
|
from cubevis.bokeh.utils import svg_icon
|
|
40
40
|
from bokeh.io import reset_output as reset_bokeh_output
|
|
41
41
|
from bokeh.io import output_notebook
|
|
@@ -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:
|
|
@@ -149,12 +149,6 @@ class CreateMask:
|
|
|
149
149
|
if False a mask path which does not exist results in an exception
|
|
150
150
|
'''
|
|
151
151
|
|
|
152
|
-
###
|
|
153
|
-
### Create application context (which includes a temporary directory).
|
|
154
|
-
### This sets the title of the plot.
|
|
155
|
-
###
|
|
156
|
-
self._app_state = AppContext( 'Create Mask' )
|
|
157
|
-
|
|
158
152
|
###
|
|
159
153
|
### widgets shared across image tabs (masking multiple images)
|
|
160
154
|
###
|
|
@@ -243,23 +237,7 @@ class CreateMask:
|
|
|
243
237
|
### If debugging this, make only a small change before confirming that exit from the
|
|
244
238
|
### Python asyncio loop continues to work... seems to be fiddly
|
|
245
239
|
###
|
|
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
|
|
240
|
+
imdetails['gui']['cube'] = CubeMask( paths[0], mask=paths[1] )
|
|
263
241
|
imdetails['image-channels'] = imdetails['gui']['cube'].shape( )[3]
|
|
264
242
|
|
|
265
243
|
|
|
@@ -276,6 +254,23 @@ class CreateMask:
|
|
|
276
254
|
self._fig['status'] = imdetails['gui']['status'] = imdetails['gui']['cube'].status_text( "<p>initialization</p>" , width=230, reuse=self._fig['status'] )
|
|
277
255
|
self._image_bitmask_controls = imdetails['gui']['cube'].bitmask_ctrl( reuse=self._image_bitmask_controls, button_type='light' )
|
|
278
256
|
|
|
257
|
+
|
|
258
|
+
,
|
|
259
|
+
imdetails['gui']['cube'].init_script = None if initialization_registered else \
|
|
260
|
+
CustomJS( args={ 'status': self._fig['status'] },
|
|
261
|
+
### app state is found based on one of the GUI's models
|
|
262
|
+
code='''const appstate = Bokeh.find.appState(status)
|
|
263
|
+
window.addEventListener( 'beforeunload',
|
|
264
|
+
(event) => {
|
|
265
|
+
function donePromise( ) {
|
|
266
|
+
return new Promise( (resolve,reject) => {
|
|
267
|
+
appstate?.cube_done?.(null,resolve)
|
|
268
|
+
} )
|
|
269
|
+
}
|
|
270
|
+
( async () => { await donePromise( ) } )( )
|
|
271
|
+
} )''' )
|
|
272
|
+
initialization_registered = True
|
|
273
|
+
|
|
279
274
|
###
|
|
280
275
|
### spectrum plot must be disabled during iteration due to "tap to change channel" functionality
|
|
281
276
|
###
|
|
@@ -319,7 +314,8 @@ class CreateMask:
|
|
|
319
314
|
|
|
320
315
|
self._image_control_tab_groups[imid] = result
|
|
321
316
|
result.js_on_change( 'active', CustomJS( args={ },
|
|
322
|
-
code='''
|
|
317
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
318
|
+
appstate.last_control_tab = cb_obj.active''' ) )
|
|
323
319
|
return result
|
|
324
320
|
|
|
325
321
|
def _create_image_panel( self, imagetuple ):
|
|
@@ -356,7 +352,8 @@ class CreateMask:
|
|
|
356
352
|
|
|
357
353
|
self._ctrl_state['stop'].js_on_click( CustomJS( args={ },
|
|
358
354
|
code='''if ( confirm( "Are you sure you want to end this mask creation session and close the GUI?" ) ) {
|
|
359
|
-
|
|
355
|
+
const appstate = Bokeh.find.appState(cb_obj)
|
|
356
|
+
appstate?.cube_done?.( )
|
|
360
357
|
}''' ) )
|
|
361
358
|
|
|
362
359
|
|
|
@@ -368,17 +365,21 @@ class CreateMask:
|
|
|
368
365
|
|
|
369
366
|
image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
|
|
370
367
|
|
|
371
|
-
self._fig['layout'] =
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
368
|
+
self._fig['layout'] = BokehAppContext(
|
|
369
|
+
column( row( self._fig['help'],
|
|
370
|
+
Spacer( height=self._ctrl_state['stop'].height, sizing_mode="scale_width" ),
|
|
371
|
+
Div( text="<div><b>status:</b></div>" ),
|
|
372
|
+
self._fig['status'],
|
|
373
|
+
self._ctrl_state['stop'], sizing_mode="scale_width" ),
|
|
374
|
+
row( image_tabs, height_policy='max', width_policy='max' ),
|
|
375
|
+
height_policy='max', width_policy='max' ),
|
|
376
|
+
app_state={ ### while the state dictionary itself
|
|
377
|
+
'name': 'create mask', ### is used, these particular element
|
|
378
|
+
'initialized': True ### are not currently used for anything
|
|
379
|
+
}, title='Create Mask' )
|
|
379
380
|
|
|
380
381
|
###
|
|
381
|
-
### Keep track of which image is currently active in
|
|
382
|
+
### Keep track of which image is currently active in appstate.image_name (which is
|
|
382
383
|
### initialized in self._js['initialize']). Also, update the current control sub-tab
|
|
383
384
|
### when the field main-tab is changed. An attempt to manage this all within the
|
|
384
385
|
### control sub-tabs using a reference to self._image_control_tab_groups from
|
|
@@ -388,11 +389,12 @@ class CreateMask:
|
|
|
388
389
|
###
|
|
389
390
|
image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._mask_state.items( ) ],
|
|
390
391
|
itergroups=self._image_control_tab_groups ),
|
|
391
|
-
code='''
|
|
392
|
-
|
|
392
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
393
|
+
if ( ! hasprop(appstate,'last_control_tab') ) {
|
|
394
|
+
appstate.last_control_tab = 0
|
|
393
395
|
}
|
|
394
|
-
|
|
395
|
-
itergroups[
|
|
396
|
+
appstate.image_name = names[cb_obj.active]
|
|
397
|
+
itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
|
|
396
398
|
|
|
397
399
|
# Change display type depending on runtime environment
|
|
398
400
|
if is_interactive_jupyter( ):
|
|
@@ -403,7 +405,7 @@ class CreateMask:
|
|
|
403
405
|
### output_file(self._imagename+'_webpage/index.html')
|
|
404
406
|
pass
|
|
405
407
|
|
|
406
|
-
|
|
408
|
+
self._fig['layout'].show( )
|
|
407
409
|
|
|
408
410
|
def _asyncio_loop( self ):
|
|
409
411
|
'''return the event loop which can be mixed in with an existing event loop
|
|
@@ -35,7 +35,7 @@ from contextlib import asynccontextmanager
|
|
|
35
35
|
from bokeh.layouts import row, column, grid
|
|
36
36
|
from bokeh.plotting import show
|
|
37
37
|
from bokeh.models import Button, CustomJS, TabPanel, Tabs, Spacer, Div, Dropdown
|
|
38
|
-
from cubevis.toolbox import CubeMask,
|
|
38
|
+
from cubevis.toolbox import CubeMask, RegionList
|
|
39
39
|
from cubevis.bokeh.utils import svg_icon
|
|
40
40
|
from bokeh.io import curdoc
|
|
41
41
|
from bokeh.io import reset_output as reset_bokeh_output
|
|
@@ -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:
|
|
@@ -141,12 +141,6 @@ class CreateRegion:
|
|
|
141
141
|
path(s) to CASA image for which interactive regions will be drawn
|
|
142
142
|
'''
|
|
143
143
|
|
|
144
|
-
###
|
|
145
|
-
### Create application context (which includes a temporary directory).
|
|
146
|
-
### This sets the title of the plot.
|
|
147
|
-
###
|
|
148
|
-
self._app_state = AppContext( 'Create Region' )
|
|
149
|
-
|
|
150
144
|
###
|
|
151
145
|
### widgets shared across image tabs (masking multiple images)
|
|
152
146
|
###
|
|
@@ -190,7 +184,7 @@ class CreateRegion:
|
|
|
190
184
|
|
|
191
185
|
###
|
|
192
186
|
### Use CubeMask init_script to set up a 'beforeunload' handler which will signal to
|
|
193
|
-
### CubeMask that the app is shuting down (with '
|
|
187
|
+
### CubeMask that the app is shuting down (with 'appstate.cube_done( )'). It should then
|
|
194
188
|
### send the final results to Python and then call the provided callback (the Promise
|
|
195
189
|
### resolve function).
|
|
196
190
|
###
|
|
@@ -231,7 +225,8 @@ class CreateRegion:
|
|
|
231
225
|
imdetails['gui']['slider'] = None
|
|
232
226
|
imdetails['gui']['goto'] = None
|
|
233
227
|
|
|
234
|
-
init_args = { '
|
|
228
|
+
init_args = { 'status': self._fig['status'],
|
|
229
|
+
'sources': { } }
|
|
235
230
|
last_cube = None
|
|
236
231
|
for k,v in self._region_state.items( ):
|
|
237
232
|
init_args['sources'][k] = v['gui']['image']['src']
|
|
@@ -246,8 +241,9 @@ class CreateRegion:
|
|
|
246
241
|
acc[src.id] = img
|
|
247
242
|
if ( source == null ) source = src
|
|
248
243
|
return acc }}, sources, {{ }} )
|
|
249
|
-
|
|
250
|
-
|
|
244
|
+
const appstate = Bokeh.find.appState(status)
|
|
245
|
+
if ( source && appstate?.cube_done )
|
|
246
|
+
appstate.cube_done(
|
|
251
247
|
casalib.reduce(
|
|
252
248
|
(acc, poly) => {{
|
|
253
249
|
acc[srcmap[poly.source.id]][poly.label] = {{
|
|
@@ -265,8 +261,8 @@ class CreateRegion:
|
|
|
265
261
|
## ImageDataSources (which is an element of CubeMask). This resulted in a circular
|
|
266
262
|
## reference when the plot was being rendered.
|
|
267
263
|
##
|
|
268
|
-
## The
|
|
269
|
-
## return `false` (indicating that the window should not be closed by
|
|
264
|
+
## The appstate.cube_done callback ( `(msg) => { resolve(true); return false }` ) can
|
|
265
|
+
## return `false` (indicating that the window should not be closed by cube_done)
|
|
270
266
|
## because the window is already being closed when this beforeunload callback
|
|
271
267
|
## is called.
|
|
272
268
|
##
|
|
@@ -385,7 +381,8 @@ class CreateRegion:
|
|
|
385
381
|
|
|
386
382
|
self._image_control_tab_groups[imid] = result
|
|
387
383
|
result.js_on_change( 'active', CustomJS( args=dict( ),
|
|
388
|
-
code='''
|
|
384
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
385
|
+
appstate.last_control_tab = cb_obj.active''' ) )
|
|
389
386
|
return result
|
|
390
387
|
|
|
391
388
|
def _create_image_panel( self, imagetuple ):
|
|
@@ -421,17 +418,21 @@ class CreateRegion:
|
|
|
421
418
|
|
|
422
419
|
image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
|
|
423
420
|
|
|
424
|
-
self._fig['layout'] =
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
421
|
+
self._fig['layout'] = BokehAppContext(
|
|
422
|
+
column( row( self._fig['help'],
|
|
423
|
+
Spacer( height=self._ctrl_state['stop'].height, sizing_mode="scale_width" ),
|
|
424
|
+
Div( text="<div><b>status:</b></div>" ),
|
|
425
|
+
self._fig['status'],
|
|
426
|
+
self._ctrl_state['stop'], sizing_mode="scale_width" ),
|
|
427
|
+
row( image_tabs, height_policy='max', width_policy='max' ),
|
|
428
|
+
height_policy='max', width_policy='max' ),
|
|
429
|
+
app_state={ ### while the state dictionary itself
|
|
430
|
+
'name': 'create region', ### is used, these particular element
|
|
431
|
+
'initialized': True ### are not currently used for anything
|
|
432
|
+
}, title='Create Region' )
|
|
432
433
|
|
|
433
434
|
###
|
|
434
|
-
### Keep track of which image is currently active in
|
|
435
|
+
### Keep track of which image is currently active in appstate.image_name (which is
|
|
435
436
|
### initialized in self._js['initialize']). Also, update the current control sub-tab
|
|
436
437
|
### when the field main-tab is changed. An attempt to manage this all within the
|
|
437
438
|
### control sub-tabs using a reference to self._image_control_tab_groups from
|
|
@@ -441,11 +442,12 @@ class CreateRegion:
|
|
|
441
442
|
###
|
|
442
443
|
image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._region_state.items( ) ],
|
|
443
444
|
itergroups=self._image_control_tab_groups ),
|
|
444
|
-
code='''
|
|
445
|
-
|
|
445
|
+
code='''const appstate = Bokeh.find.appState(cb_obj)
|
|
446
|
+
if ( ! hasprop(appstate,'last_control_tab') ) {
|
|
447
|
+
appstate.last_control_tab = 0
|
|
446
448
|
}
|
|
447
|
-
|
|
448
|
-
itergroups[
|
|
449
|
+
appstate.image_name = names[cb_obj.active]
|
|
450
|
+
itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
|
|
449
451
|
|
|
450
452
|
# Change display type depending on runtime environment
|
|
451
453
|
if is_interactive_jupyter( ):
|
|
@@ -456,7 +458,7 @@ class CreateRegion:
|
|
|
456
458
|
### output_file(self._imagename+'_webpage/index.html')
|
|
457
459
|
pass
|
|
458
460
|
|
|
459
|
-
|
|
461
|
+
self._fig['layout'].show( )
|
|
460
462
|
|
|
461
463
|
def _asyncio_loop( self ):
|
|
462
464
|
'''return the event loop which can be mixed in with an existing event loop
|
|
@@ -87,7 +87,8 @@ class InteractiveClean:
|
|
|
87
87
|
interpolation='nearest', ... )( ) )
|
|
88
88
|
'''
|
|
89
89
|
self._id = uuid4( )
|
|
90
|
+
self._ui.exclusion_mgr.set_mode("tab")
|
|
90
91
|
context = exe.Context( exe.Mode.SYNC )
|
|
91
92
|
bokeh_ui, exec_task = self._ui( context, self._id )
|
|
92
|
-
show(
|
|
93
|
+
bokeh_ui.show( )
|
|
93
94
|
return context.execute( exec_task, self._id )
|
|
@@ -1849,7 +1849,8 @@ class InteractiveClean:
|
|
|
1849
1849
|
interpolation='nearest', ... )( ) )
|
|
1850
1850
|
'''
|
|
1851
1851
|
self._id = uuid4( )
|
|
1852
|
+
self._ui.exclusion_mgr.set_mode("tab")
|
|
1852
1853
|
context = exe.Context( exe.Mode.SYNC )
|
|
1853
1854
|
bokeh_ui, exec_task = self._ui( context, self._id )
|
|
1854
|
-
show(
|
|
1855
|
+
bokeh_ui.show( )
|
|
1855
1856
|
return context.execute( exec_task, self._id )
|
|
@@ -43,6 +43,22 @@ from cubevis.utils import find_pkg, load_pkg
|
|
|
43
43
|
from cubevis.toolbox import InteractiveCleanUI
|
|
44
44
|
from cubevis import exe
|
|
45
45
|
|
|
46
|
+
class DisplayContext:
|
|
47
|
+
def __init__(self, exclusion_manager):
|
|
48
|
+
self.exclusion_manager = exclusion_manager
|
|
49
|
+
self._custom_show_called = False
|
|
50
|
+
|
|
51
|
+
def on_show(self):
|
|
52
|
+
self._custom_show_called = True
|
|
53
|
+
self.exclusion_manager.set_mode('cell-custom-show')
|
|
54
|
+
|
|
55
|
+
def on_to_serializable(self):
|
|
56
|
+
# Only set mode if custom_show hasn't been called
|
|
57
|
+
if not self._custom_show_called:
|
|
58
|
+
self.exclusion_manager.set_mode('cell-bokeh-show')
|
|
59
|
+
# If custom_show was called, we're already in 'cell-custom-show' mode
|
|
60
|
+
# and don't need to do anything
|
|
61
|
+
|
|
46
62
|
class InteractiveCleanNotebook:
|
|
47
63
|
r'''InteractiveCleanNotebook(...) implements interactive clean using Bokeh
|
|
48
64
|
{{docstring}}
|
|
@@ -107,6 +123,13 @@ class InteractiveCleanNotebook:
|
|
|
107
123
|
def startup( ):
|
|
108
124
|
self._future = context.execute( exec_task, self._id )
|
|
109
125
|
|
|
126
|
+
display_ctx = DisplayContext(self._ui.exclusion_mgr)
|
|
110
127
|
### name is used in summary output of the Showable
|
|
111
|
-
showed = Showable(
|
|
128
|
+
showed = Showable(
|
|
129
|
+
bokeh_ui,
|
|
130
|
+
startup,
|
|
131
|
+
self.get_future,
|
|
132
|
+
name="iclean-jpy",
|
|
133
|
+
display_context=display_ctx
|
|
134
|
+
)
|
|
112
135
|
return showed
|
|
@@ -42,6 +42,22 @@ from cubevis.utils import find_pkg, load_pkg
|
|
|
42
42
|
from cubevis.toolbox import InteractiveCleanUI
|
|
43
43
|
from cubevis import exe
|
|
44
44
|
|
|
45
|
+
class DisplayContext:
|
|
46
|
+
def __init__(self, exclusion_manager):
|
|
47
|
+
self.exclusion_manager = exclusion_manager
|
|
48
|
+
self._custom_show_called = False
|
|
49
|
+
|
|
50
|
+
def on_show(self):
|
|
51
|
+
self._custom_show_called = True
|
|
52
|
+
self.exclusion_manager.set_mode('cell-custom-show')
|
|
53
|
+
|
|
54
|
+
def on_to_serializable(self):
|
|
55
|
+
# Only set mode if custom_show hasn't been called
|
|
56
|
+
if not self._custom_show_called:
|
|
57
|
+
self.exclusion_manager.set_mode('cell-bokeh-show')
|
|
58
|
+
# If custom_show was called, we're already in 'cell-custom-show' mode
|
|
59
|
+
# and don't need to do anything
|
|
60
|
+
|
|
45
61
|
class InteractiveCleanNotebook:
|
|
46
62
|
r'''InteractiveCleanNotebook(...) implements interactive clean using Bokeh
|
|
47
63
|
tclean ---- Radio Interferometric Image Reconstruction
|
|
@@ -1869,6 +1885,13 @@ class InteractiveCleanNotebook:
|
|
|
1869
1885
|
def startup( ):
|
|
1870
1886
|
self._future = context.execute( exec_task, self._id )
|
|
1871
1887
|
|
|
1888
|
+
display_ctx = DisplayContext(self._ui.exclusion_mgr)
|
|
1872
1889
|
### name is used in summary output of the Showable
|
|
1873
|
-
showed = Showable(
|
|
1890
|
+
showed = Showable(
|
|
1891
|
+
bokeh_ui,
|
|
1892
|
+
startup,
|
|
1893
|
+
self.get_future,
|
|
1894
|
+
name="iclean-jpy",
|
|
1895
|
+
display_context=display_ctx
|
|
1896
|
+
)
|
|
1874
1897
|
return showed
|
cubevis/toolbox/__init__.py
CHANGED
cubevis/toolbox/_cube.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#######################################################################
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2022,2023,2024
|
|
3
|
+
# Copyright (C) 2022,2023,2024,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
|
|
@@ -1488,20 +1488,24 @@ 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
|
+
const showable = Bokeh.find.showable(image)
|
|
1498
|
+
appstate.cube_already_shutdown = false
|
|
1499
|
+
''' + self._js['mask-state-init'] +
|
|
1497
1500
|
( self._js['func-curmasks']( ) +
|
|
1498
1501
|
self._js['key-state-funcs']
|
|
1499
1502
|
if self._mask_path is None else '' ) +
|
|
1500
1503
|
self._js['update-status'] + self._js['setup-key-mgmt'] +
|
|
1501
1504
|
"""// This function is called to collect the masks and/or stop
|
|
1502
1505
|
// -->> collect_masks( ) is only defined if bitmask cube is NOT used
|
|
1503
|
-
|
|
1504
|
-
|
|
1506
|
+
console.log( "App state:", appstate )
|
|
1507
|
+
appstate.cube_done = ( final_polys=null, cb=null ) => {
|
|
1508
|
+
if ( appstate.cube_already_shutdown ) return
|
|
1505
1509
|
function done_close_window( msg ) {
|
|
1506
1510
|
if ( msg.result === 'stopped' ) {""" +
|
|
1507
1511
|
# Don't close tab if running in a jupyter notebook
|
|
@@ -1509,14 +1513,28 @@ class CubeMask:
|
|
|
1509
1513
|
## """console.log("Running from script/terminal. Closing window.")
|
|
1510
1514
|
## window.close()"""
|
|
1511
1515
|
##) +
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
+
###
|
|
1517
|
+
### Previously the Python is_interactive_jupyter( ) function was used
|
|
1518
|
+
### to decide whether the window should be closed or not. However, to
|
|
1519
|
+
### support the use of the regular iclean (i.e. display in a separate
|
|
1520
|
+
### browser tab) the check is now whether a showable is available or
|
|
1521
|
+
### not since Showable is the coupling between GUIs and Jupyter
|
|
1522
|
+
### notebooks.
|
|
1523
|
+
###
|
|
1524
|
+
"""if ( showable ) {
|
|
1525
|
+
console.log("Running in jupyter notebook.\\nDisabling GUI via root Showable.")
|
|
1526
|
+
showable.disabled = true
|
|
1527
|
+
const data_pipes = Bokeh.activeDataPipes.getInstances( )
|
|
1528
|
+
console.log("Data pipes to be closed:", data_pipes)
|
|
1529
|
+
} else {
|
|
1530
|
+
console.log("Running from script/terminal. Closing window.")
|
|
1531
|
+
window.close()
|
|
1532
|
+
}
|
|
1533
|
+
""" +
|
|
1516
1534
|
"""
|
|
1517
1535
|
}
|
|
1518
1536
|
}
|
|
1519
|
-
|
|
1537
|
+
appstate.cube_already_shutdown = true
|
|
1520
1538
|
ctrl.send( ids['done'],
|
|
1521
1539
|
{ action: 'done',
|
|
1522
1540
|
value: { regions: final_polys ? final_polys : source.masks( ) },
|
|
@@ -1526,13 +1544,12 @@ class CubeMask:
|
|
|
1526
1544
|
// exported functions -- enable/disable masking, retrieve masks etc.
|
|
1527
1545
|
source._masking_enabled = true
|
|
1528
1546
|
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 }
|
|
1547
|
+
if ( ! ('masking_state' in appstate) )
|
|
1548
|
+
appstate.masking_state = { }
|
|
1549
|
+
appstate.masking_state[source.id] = false
|
|
1550
|
+
source.masking_on = ( ) => { return appstate.masking_state[source.id] }
|
|
1551
|
+
source.disable_masking = ( ) => { source._masking_enabled = false; appstate.masking_state[source.id] = false }
|
|
1552
|
+
source.enable_masking = ( ) => { source._masking_enabled = true; appstate.masking_state[source.id] = true }
|
|
1536
1553
|
source.disable_pixel_update = ( ) => source._pixel_update_enabled = false
|
|
1537
1554
|
source.enable_pixel_update = ( ) => source._pixel_update_enabled = true
|
|
1538
1555
|
source.masks = ( ) => typeof collect_masks == 'function' ? collect_masks( ) : { masks: [], polys: [] }
|
|
@@ -1549,7 +1566,7 @@ class CubeMask:
|
|
|
1549
1566
|
if ( stats_source ) source.update_statistics( stats_source.data ) /*** round pre-filled floats ***/
|
|
1550
1567
|
/*** this is the hook that allows the user to disable mask changes ***/
|
|
1551
1568
|
this.disable_add_sub = disable_add_sub
|
|
1552
|
-
if ( user_init_script ) { user_init_script.execute(this) }
|
|
1569
|
+
if ( user_init_script ) { user_init_script.execute({ origin: this, appstate }) }
|
|
1553
1570
|
""" )
|
|
1554
1571
|
|
|
1555
1572
|
def region_position_connections( ):
|
|
@@ -2189,7 +2206,7 @@ class CubeMask:
|
|
|
2189
2206
|
stokes_labels=[ k for k,_ in self._region_controls['coord']['chan'].items( ) ],
|
|
2190
2207
|
status_line=self._region_controls['coord']['status'] if self._mask_path is None else None,
|
|
2191
2208
|
chan_select = [ v.child for k,v in self._region_controls['coord']['chan'].items( ) ] ),
|
|
2192
|
-
code= ( self._js['func-newpoly'] + self._js['func-curmasks']( ) +
|
|
2209
|
+
code= ( "const appstate = Bokeh.find.appState(cb_obj.origin);" + self._js['func-newpoly'] + self._js['func-curmasks']( ) +
|
|
2193
2210
|
self._js['mask-state-init'] + self._js_mode_code['no-bitmask-tool-selection'] )
|
|
2194
2211
|
if self._mask_path is None else "" + (
|
|
2195
2212
|
### selector indicates if a on-disk mask is being used
|
|
@@ -2655,10 +2672,10 @@ class CubeMask:
|
|
|
2655
2672
|
'no-bitmask-init': '''function _create_poly_mgr( annos, stokes_labels ) {
|
|
2656
2673
|
const chan2polys = { }
|
|
2657
2674
|
|
|
2658
|
-
if ( typeof
|
|
2659
|
-
|
|
2675
|
+
if ( typeof appstate.polygon_list == 'undefined' )
|
|
2676
|
+
appstate.polygon_list = [ ]
|
|
2660
2677
|
|
|
2661
|
-
const polys =
|
|
2678
|
+
const polys = appstate.polygon_list
|
|
2662
2679
|
|
|
2663
2680
|
return {
|
|
2664
2681
|
reset_annos: ( ) => {
|