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.
@@ -49,9 +49,11 @@ from bokeh.io import reset_output as reset_bokeh_output, output_notebook
49
49
  from bokeh.models.dom import HTML
50
50
 
51
51
  from bokeh.models.ui.tooltips import Tooltip
52
- from cubevis.bokeh.models import TipButton, Tip, EvTextInput
52
+ from cubevis.bokeh.models import TipButton, Tip, EvTextInput, BokehAppContext
53
+
53
54
  from cubevis.utils import resource_manager, reset_resource_manager, is_interactive_jupyter, find_pkg, load_pkg
54
55
  from cubevis.utils import ContextMgrChain as CMC
56
+ from cubevis.utils import MutualExclusionManager
55
57
 
56
58
  # pylint: disable=no-name-in-module
57
59
  from casatasks.private.imagerhelpers.imager_return_dict import ImagingDict
@@ -60,7 +62,7 @@ from casatasks.private.imagerhelpers.input_parameters import ImagerParameters
60
62
  # pylint: enable=no-name-in-module
61
63
 
62
64
  from cubevis.utils import find_ws_address, convert_masks
63
- from cubevis.toolbox import CubeMask, AppContext
65
+ from cubevis.toolbox import CubeMask
64
66
  from cubevis.bokeh.utils import svg_icon
65
67
  from cubevis.bokeh.sources import DataPipe
66
68
  from cubevis.utils import DocEnum
@@ -73,6 +75,25 @@ USE_MULTIPLE_GCLEAN_HACK=False
73
75
  class InteractiveCleanUI:
74
76
  '''InteractiveCleanUI(...) implements interactive clean using Bokeh
75
77
  '''
78
+
79
+ exclusion_mgr = MutualExclusionManager(
80
+ name="interactive clean",
81
+ valid_modes={
82
+ "tab": "❌ Cannot use iclean task display:\n\n" \
83
+ "Reason: bokeh.plotting.show() or iclean show method has already been\n" \
84
+ " used for display of this class. Mixing display methods within\n" \
85
+ " a single notebook corrupts Bokeh display within the notebook\n",
86
+ "cell-bokeh-show": "❌ Cannot use bokeh.plotting.show() display method:\n\n" \
87
+ "Reason: iclean show or task method has already been used for display\n" \
88
+ " of this class. Mixing display methods within a single notebook\n" \
89
+ " corrupts Bokeh display within the notebook\n",
90
+ "cell-custom-show": "❌ Cannot use iclean show method:\n\n" \
91
+ "Reason: bokeh.plotting.show() or task has already been used for display\n" \
92
+ " of this class. Mixing display methods within a single notebook\n" \
93
+ " corrupts Bokeh display within the notebook\n",
94
+ }
95
+ )
96
+
76
97
  def __stop( self, _=None ):
77
98
  self.__result_future.set_result(self.__retrieve_result( ))
78
99
 
@@ -163,11 +184,6 @@ class InteractiveCleanUI:
163
184
  ### the image display.
164
185
  ###
165
186
  self._conv_spect_plot_width = 450
166
- ###
167
- ### Create application context (which includes a temporary directory).
168
- ### This sets the title of the plot.
169
- ###
170
- self._app_state = AppContext( 'Interactive Clean' )
171
187
 
172
188
  ###
173
189
  ### Whether or not the Interactive Clean session is running remotely
@@ -301,11 +317,13 @@ class InteractiveCleanUI:
301
317
  init_script=CustomJS( args=dict( initial_convergence_state=self._init_values["convergence_state"],
302
318
  clean_ctrl=self._control['iteration'],
303
319
  name=imid ),
304
- code='''document._casa_convergence_data = initial_convergence_state
305
- clean_ctrl.continue.disable_add_sub = this.disable_add_sub.values
306
- clean_ctrl.finish.disable_add_sub = this.disable_add_sub.values
307
- clean_ctrl.stop.disable_add_sub = this.disable_add_sub.values
308
- this.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
320
+ ### save appstate as _appstate in image source for future access
321
+ code='''cb_obj.appstate.convergence_data = initial_convergence_state
322
+ cb_obj.origin._appstate = cb_obj.appstate
323
+ clean_ctrl.continue.disable_add_sub = cb_obj.origin.disable_add_sub.values
324
+ clean_ctrl.finish.disable_add_sub = cb_obj.origin.disable_add_sub.values
325
+ clean_ctrl.stop.disable_add_sub = cb_obj.origin.disable_add_sub.values
326
+ cb_obj.origin.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
309
327
  if idx == 0 else None )
310
328
 
311
329
  ###
@@ -677,7 +695,7 @@ class InteractiveCleanUI:
677
695
  ctrl={ 'converge': self._clean['converge'] },
678
696
  stopdescmap=ImagingDict.get_summaryminor_stopdesc( ) ),
679
697
  code=self._js['update-converge'] +
680
- '''update_convergence_single( img_state, document._casa_convergence_data.convergence[imid] )''' ) )
698
+ '''update_convergence_single( img_state, cb_obj._appstate.convergence_data.convergence[imid] )''' ) )
681
699
 
682
700
  ###
683
701
  ### collect toolbars for syncing selection
@@ -809,7 +827,8 @@ class InteractiveCleanUI:
809
827
  logbutton=self._log_button,
810
828
  stopdescmap=ImagingDict.get_summaryminor_stopdesc( )
811
829
  ),
812
- code=self._js['update-converge'] + self._js['clean-refresh'] + self._js['clean-disable'] +
830
+ code="const appstate = Bokeh.find.appState(cb_obj.origin);" +
831
+ self._js['update-converge'] + self._js['clean-refresh'] + self._js['clean-disable'] +
813
832
  self._js['clean-enable'] + self._js['clean-status-update'] +
814
833
  self._js['iter-gui-update'] + self._js['clean-wait'] +
815
834
  '''function invalid_niter( s ) {
@@ -858,11 +877,11 @@ class InteractiveCleanUI:
858
877
  // { action: 'stop',
859
878
  // value: { } },
860
879
  // update_gui )
861
- document._casa_window_closed = true
880
+ appstate.window_closed = true
862
881
  /*** this will close the tab >>>>---------+ ***/
863
882
  /*** | ***/
864
- /*** vvvvv----------------------------+ ***/
865
- document._cube_done( Object.entries(images_state).reduce((acc,[k,v]) => ({ ...acc, [k]: v.src.masks( ) }),{ } ) )
883
+ /*** vvvvvvvvv-----------------------+ ***/
884
+ appstate.cube_done( Object.entries(images_state).reduce((acc,[k,v]) => ({ ...acc, [k]: v.src.masks( ) }),{ } ) )
866
885
  }
867
886
  } else if ( state.mode === 'continuous' &&
868
887
  cb_obj.origin.name === 'stop' &&
@@ -932,18 +951,22 @@ class InteractiveCleanUI:
932
951
 
933
952
  image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
934
953
 
935
- self._fig['layout'] = column(
936
- row( help_button,
937
- self._log_button,
938
- Spacer( height=self._control['iteration']['stop'].height, sizing_mode="scale_width" ),
939
- Div( text="<div><b>status:</b></div>" ),
940
- status_line,
941
- self._control['iteration']['stop'], self._control['iteration']['continue'], self._control['iteration']['finish'], sizing_mode="scale_width" ),
942
- row( image_tabs, height_policy='max', width_policy='max' ),
943
- height_policy='max', width_policy='max' )
954
+ self._fig['layout'] = BokehAppContext(
955
+ column( row( help_button,
956
+ self._log_button,
957
+ Spacer( height=self._control['iteration']['stop'].height, sizing_mode="scale_width" ),
958
+ Div( text="<div><b>status:</b></div>" ),
959
+ status_line,
960
+ self._control['iteration']['stop'], self._control['iteration']['continue'], self._control['iteration']['finish'], sizing_mode="scale_width" ),
961
+ row( image_tabs, height_policy='max', width_policy='max' ),
962
+ height_policy='max', width_policy='max' ),
963
+ app_state={ ### while the state dictionary itself
964
+ 'name': 'interactive clean', ### is used, these particular element
965
+ 'initialized': True ### are not currently used for anything
966
+ }, title='Interactive Clean' )
944
967
 
945
968
  ###
946
- ### Keep track of which image is currently active in document._casa_image_name (which is
969
+ ### Keep track of which image is currently active in appstate.image_name (which is
947
970
  ### initialized in self._js['initialize']). Also, update the current control sub-tab
948
971
  ### when the field main-tab is changed. An attempt to manage this all within the
949
972
  ### control sub-tabs using a reference to self._image_control_tab_groups from
@@ -951,13 +974,17 @@ class InteractiveCleanUI:
951
974
  ###
952
975
  ### bokeh.core.serialization.SerializationError: circular reference
953
976
  ###
977
+ ### This set of tabs is the primary, outer tabs where each tab contains the complete
978
+ ### set of image controls
979
+ ###
954
980
  image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._clean_targets.items( ) ],
955
981
  itergroups=self._image_control_tab_groups ),
956
- code='''if ( ! hasprop(document,'_casa_last_control_tab') ) {
957
- document._casa_last_control_tab = 0
982
+ code='''const appstate = Bokeh.find.appState(cb_obj)
983
+ if ( ! hasprop(appstate,'last_control_tab') ) {
984
+ appstate.last_control_tab = 0
958
985
  }
959
- document._casa_image_name = names[cb_obj.active]
960
- itergroups[document._casa_image_name].active = document._casa_last_control_tab''' ) )
986
+ appstate.image_name = names[cb_obj.active]
987
+ itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
961
988
 
962
989
  # Change display type depending on runtime environment
963
990
  #if self._is_notebook:
@@ -1012,8 +1039,11 @@ class InteractiveCleanUI:
1012
1039
  self._image_control_tab_groups = { }
1013
1040
 
1014
1041
  self._image_control_tab_groups[imid] = result
1042
+
1043
+ ### This set of tabs is the secondary, inner tabs for different controls for an individual image
1015
1044
  result.js_on_change( 'active', CustomJS( args=dict( ),
1016
- code='''document._casa_last_control_tab = cb_obj.active''' ) )
1045
+ code='''const appstate = Bokeh.find.appState(cb_obj)
1046
+ appstate.last_control_tab = cb_obj.active''' ) )
1017
1047
  return result
1018
1048
 
1019
1049
  def _create_image_panel( self, imagetuple ):
@@ -1280,20 +1310,22 @@ class InteractiveCleanUI:
1280
1310
 
1281
1311
  def _initialize_javascript( self ):
1282
1312
  self._js = { ### initialize state
1283
- ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1284
- ### -- document is used storing state --
1285
- ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1286
- 'initialize': '''if ( ! document._casa_initialized ) {
1313
+ ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1314
+ ### -- "logbutton" is a random GUI model which can be used to locate the --
1315
+ ### -- appstate that is used storing state --
1316
+ ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1317
+ 'initialize': '''const appstate = Bokeh.find.appState(logbutton)
1318
+ if ( ! appstate.ic_initialized ) {
1287
1319
  console.log(`casalib version: ${casalib.version}`)
1288
- document._casa_image_name = initial_image
1289
- document._casa_initialized = true
1290
- document._casa_window_closed = false
1320
+ appstate.image_name = initial_image
1321
+ appstate.ic_initialized = true
1322
+ appstate.window_closed = false
1291
1323
  window.addEventListener( 'beforeunload',
1292
1324
  function (e) {
1293
1325
  // if the window is already closed this message is never
1294
1326
  // delivered (unless interactive clean is called again then
1295
1327
  // the event shows up in the newly created control pipe)
1296
- if ( document._casa_window_closed == false ) {
1328
+ if ( appstate.window_closed == false ) {
1297
1329
  ctrl_pipe.send( ids['stop'],
1298
1330
  { action: 'stop', value: { } },
1299
1331
  undefined ) } } )
@@ -1369,13 +1401,13 @@ class InteractiveCleanUI:
1369
1401
 
1370
1402
  function update_convergence( recurse=false ) {
1371
1403
  let convdata
1372
- if ( hasprop(document,'_casa_convergence_data') ) {
1373
- convdata = document._casa_convergence_data
1404
+ if ( hasprop(appstate,'convergence_data') ) {
1405
+ convdata = appstate.convergence_data
1374
1406
  } else {
1375
1407
  if ( ! recurse ) {
1376
1408
  ctrl.converge.pipe.send( ctrl.converge.id, { action: 'retrieve' },
1377
1409
  (msg) => { if ( hasprop( msg.result, 'convergence' ) ) {
1378
- document._casa_convergence_data = { convergence: msg.result.convergence,
1410
+ appstate.convergence_data = { convergence: msg.result.convergence,
1379
1411
  cyclethreshold: msg.result.cyclethreshold }
1380
1412
  update_convergence(true)
1381
1413
  } } )
@@ -1410,8 +1442,8 @@ class InteractiveCleanUI:
1410
1442
  }
1411
1443
 
1412
1444
  if ( hasprop(clean_msg,'convergence') && clean_msg.convergence != null ) {
1413
- document._casa_convergence_data = { convergence: clean_msg.convergence,
1414
- cyclethreshold: clean_msg.cyclethreshold }
1445
+ appstate.convergence_data = { convergence: clean_msg.convergence,
1446
+ cyclethreshold: clean_msg.cyclethreshold }
1415
1447
  }
1416
1448
  }
1417
1449
 
@@ -1493,19 +1525,19 @@ class InteractiveCleanUI:
1493
1525
  'Stopping criteria encountered',
1494
1526
  'Unrecognized stop code' ]
1495
1527
  if ( typeof status === 'number' ) {
1496
- images_state[document._casa_image_name]['status'].text =
1528
+ images_state[appstate.image_name]['status'].text =
1497
1529
  '<p>' +
1498
1530
  stopstr[ status < 0 || status >= stopstr.length ?
1499
1531
  stopstr.length - 1 : status ] +
1500
1532
  '</p>'
1501
1533
  } else {
1502
- images_state[document._casa_image_name]['status'].text = `<p>${status}</p>`
1534
+ images_state[appstate.image_name]['status'].text = `<p>${status}</p>`
1503
1535
  }
1504
1536
  }''',
1505
1537
 
1506
1538
  'iter-gui-update': '''function get_update_dictionary( ) {
1507
- //const amste = images_state[document._casa_image_name]['automask']
1508
- //const clste = images_state[document._casa_image_name]['iteration']
1539
+ //const amste = images_state[appstate.image_name]['automask']
1540
+ //const clste = images_state[appstate.image_name]['iteration']
1509
1541
  // Assumption is that there is ONE set of iteration and automask updates
1510
1542
  // for ALL imaging fields...
1511
1543
  const amobj = Object.entries(images_state)[0][1].automask
@@ -1524,7 +1556,7 @@ class InteractiveCleanUI:
1524
1556
 
1525
1557
  const masks = Object.entries(images_state).reduce( (acc,[k,v]) => { acc[k] = v.src.masks( ); return acc }, { } )
1526
1558
  const breadcrumbs = Object.entries(images_state).reduce( (acc,[k,v]) => { acc[k] = v.src.breadcrumbs( ); return acc }, { } )
1527
- return { iteration, automask, masks, breadcrumbs, current_image: document._casa_image_name }
1559
+ return { iteration, automask, masks, breadcrumbs, current_image: appstate.image_name }
1528
1560
  }
1529
1561
  function update_log( log_lines ) {
1530
1562
  let b = logbutton
@@ -48,9 +48,11 @@ from bokeh.io import reset_output as reset_bokeh_output, output_notebook
48
48
  from bokeh.models.dom import HTML
49
49
 
50
50
  from bokeh.models.ui.tooltips import Tooltip
51
- from cubevis.bokeh.models import TipButton, Tip, EvTextInput
51
+ from cubevis.bokeh.models import TipButton, Tip, EvTextInput, BokehAppContext
52
+
52
53
  from cubevis.utils import resource_manager, reset_resource_manager, is_interactive_jupyter, find_pkg, load_pkg
53
54
  from cubevis.utils import ContextMgrChain as CMC
55
+ from cubevis.utils import MutualExclusionManager
54
56
 
55
57
  # pylint: disable=no-name-in-module
56
58
  from casatasks.private.imagerhelpers.imager_return_dict import ImagingDict
@@ -59,7 +61,7 @@ from casatasks.private.imagerhelpers.input_parameters import ImagerParameters
59
61
  # pylint: enable=no-name-in-module
60
62
 
61
63
  from cubevis.utils import find_ws_address, convert_masks
62
- from cubevis.toolbox import CubeMask, AppContext
64
+ from cubevis.toolbox import CubeMask
63
65
  from cubevis.bokeh.utils import svg_icon
64
66
  from cubevis.bokeh.sources import DataPipe
65
67
  from cubevis.utils import DocEnum
@@ -72,6 +74,25 @@ USE_MULTIPLE_GCLEAN_HACK=False
72
74
  class InteractiveCleanUI:
73
75
  '''InteractiveCleanUI(...) implements interactive clean using Bokeh
74
76
  '''
77
+
78
+ exclusion_mgr = MutualExclusionManager(
79
+ name="interactive clean",
80
+ valid_modes={
81
+ "tab": "❌ Cannot use iclean task display:\n\n" \
82
+ "Reason: bokeh.plotting.show() or iclean show method has already been\n" \
83
+ " used for display of this class. Mixing display methods within\n" \
84
+ " a single notebook corrupts Bokeh display within the notebook\n",
85
+ "cell-bokeh-show": "❌ Cannot use bokeh.plotting.show() display method:\n\n" \
86
+ "Reason: iclean show or task method has already been used for display\n" \
87
+ " of this class. Mixing display methods within a single notebook\n" \
88
+ " corrupts Bokeh display within the notebook\n",
89
+ "cell-custom-show": "❌ Cannot use iclean show method:\n\n" \
90
+ "Reason: bokeh.plotting.show() or task has already been used for display\n" \
91
+ " of this class. Mixing display methods within a single notebook\n" \
92
+ " corrupts Bokeh display within the notebook\n",
93
+ }
94
+ )
95
+
75
96
  def __stop( self, _=None ):
76
97
  self.__result_future.set_result(self.__retrieve_result( ))
77
98
 
@@ -162,11 +183,6 @@ class InteractiveCleanUI:
162
183
  ### the image display.
163
184
  ###
164
185
  self._conv_spect_plot_width = 450
165
- ###
166
- ### Create application context (which includes a temporary directory).
167
- ### This sets the title of the plot.
168
- ###
169
- self._app_state = AppContext( 'Interactive Clean' )
170
186
 
171
187
  ###
172
188
  ### Whether or not the Interactive Clean session is running remotely
@@ -300,11 +316,13 @@ class InteractiveCleanUI:
300
316
  init_script=CustomJS( args=dict( initial_convergence_state=self._init_values["convergence_state"],
301
317
  clean_ctrl=self._control['iteration'],
302
318
  name=imid ),
303
- code='''document._casa_convergence_data = initial_convergence_state
304
- clean_ctrl.continue.disable_add_sub = this.disable_add_sub.values
305
- clean_ctrl.finish.disable_add_sub = this.disable_add_sub.values
306
- clean_ctrl.stop.disable_add_sub = this.disable_add_sub.values
307
- this.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
319
+ ### save appstate as _appstate in image source for future access
320
+ code='''cb_obj.appstate.convergence_data = initial_convergence_state
321
+ cb_obj.origin._appstate = cb_obj.appstate
322
+ clean_ctrl.continue.disable_add_sub = cb_obj.origin.disable_add_sub.values
323
+ clean_ctrl.finish.disable_add_sub = cb_obj.origin.disable_add_sub.values
324
+ clean_ctrl.stop.disable_add_sub = cb_obj.origin.disable_add_sub.values
325
+ cb_obj.origin.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
308
326
  if idx == 0 else None )
309
327
 
310
328
  ###
@@ -676,7 +694,7 @@ class InteractiveCleanUI:
676
694
  ctrl={ 'converge': self._clean['converge'] },
677
695
  stopdescmap=ImagingDict.get_summaryminor_stopdesc( ) ),
678
696
  code=self._js['update-converge'] +
679
- '''update_convergence_single( img_state, document._casa_convergence_data.convergence[imid] )''' ) )
697
+ '''update_convergence_single( img_state, cb_obj._appstate.convergence_data.convergence[imid] )''' ) )
680
698
 
681
699
  ###
682
700
  ### collect toolbars for syncing selection
@@ -808,7 +826,8 @@ class InteractiveCleanUI:
808
826
  logbutton=self._log_button,
809
827
  stopdescmap=ImagingDict.get_summaryminor_stopdesc( )
810
828
  ),
811
- code=self._js['update-converge'] + self._js['clean-refresh'] + self._js['clean-disable'] +
829
+ code="const appstate = Bokeh.find.appState(cb_obj.origin);" +
830
+ self._js['update-converge'] + self._js['clean-refresh'] + self._js['clean-disable'] +
812
831
  self._js['clean-enable'] + self._js['clean-status-update'] +
813
832
  self._js['iter-gui-update'] + self._js['clean-wait'] +
814
833
  '''function invalid_niter( s ) {
@@ -857,11 +876,11 @@ class InteractiveCleanUI:
857
876
  // { action: 'stop',
858
877
  // value: { } },
859
878
  // update_gui )
860
- document._casa_window_closed = true
879
+ appstate.window_closed = true
861
880
  /*** this will close the tab >>>>---------+ ***/
862
881
  /*** | ***/
863
- /*** vvvvv----------------------------+ ***/
864
- document._cube_done( Object.entries(images_state).reduce((acc,[k,v]) => ({ ...acc, [k]: v.src.masks( ) }),{ } ) )
882
+ /*** vvvvvvvvv-----------------------+ ***/
883
+ appstate.cube_done( Object.entries(images_state).reduce((acc,[k,v]) => ({ ...acc, [k]: v.src.masks( ) }),{ } ) )
865
884
  }
866
885
  } else if ( state.mode === 'continuous' &&
867
886
  cb_obj.origin.name === 'stop' &&
@@ -931,18 +950,22 @@ class InteractiveCleanUI:
931
950
 
932
951
  image_tabs = Tabs( tabs=tab_panels, tabs_location='below', height_policy='max', width_policy='max' )
933
952
 
934
- self._fig['layout'] = column(
935
- row( help_button,
936
- self._log_button,
937
- Spacer( height=self._control['iteration']['stop'].height, sizing_mode="scale_width" ),
938
- Div( text="<div><b>status:</b></div>" ),
939
- status_line,
940
- self._control['iteration']['stop'], self._control['iteration']['continue'], self._control['iteration']['finish'], sizing_mode="scale_width" ),
941
- row( image_tabs, height_policy='max', width_policy='max' ),
942
- height_policy='max', width_policy='max' )
953
+ self._fig['layout'] = BokehAppContext(
954
+ column( row( help_button,
955
+ self._log_button,
956
+ Spacer( height=self._control['iteration']['stop'].height, sizing_mode="scale_width" ),
957
+ Div( text="<div><b>status:</b></div>" ),
958
+ status_line,
959
+ self._control['iteration']['stop'], self._control['iteration']['continue'], self._control['iteration']['finish'], sizing_mode="scale_width" ),
960
+ row( image_tabs, height_policy='max', width_policy='max' ),
961
+ height_policy='max', width_policy='max' ),
962
+ app_state={ ### while the state dictionary itself
963
+ 'name': 'interactive clean', ### is used, these particular element
964
+ 'initialized': True ### are not currently used for anything
965
+ }, title='Interactive Clean' )
943
966
 
944
967
  ###
945
- ### Keep track of which image is currently active in document._casa_image_name (which is
968
+ ### Keep track of which image is currently active in appstate.image_name (which is
946
969
  ### initialized in self._js['initialize']). Also, update the current control sub-tab
947
970
  ### when the field main-tab is changed. An attempt to manage this all within the
948
971
  ### control sub-tabs using a reference to self._image_control_tab_groups from
@@ -950,13 +973,17 @@ class InteractiveCleanUI:
950
973
  ###
951
974
  ### bokeh.core.serialization.SerializationError: circular reference
952
975
  ###
976
+ ### This set of tabs is the primary, outer tabs where each tab contains the complete
977
+ ### set of image controls
978
+ ###
953
979
  image_tabs.js_on_change( 'active', CustomJS( args=dict( names=[ t[0] for t in self._clean_targets.items( ) ],
954
980
  itergroups=self._image_control_tab_groups ),
955
- code='''if ( ! hasprop(document,'_casa_last_control_tab') ) {
956
- document._casa_last_control_tab = 0
981
+ code='''const appstate = Bokeh.find.appState(cb_obj)
982
+ if ( ! hasprop(appstate,'last_control_tab') ) {
983
+ appstate.last_control_tab = 0
957
984
  }
958
- document._casa_image_name = names[cb_obj.active]
959
- itergroups[document._casa_image_name].active = document._casa_last_control_tab''' ) )
985
+ appstate.image_name = names[cb_obj.active]
986
+ itergroups[appstate.image_name].active = appstate.last_control_tab''' ) )
960
987
 
961
988
  # Change display type depending on runtime environment
962
989
  #if self._is_notebook:
@@ -1011,8 +1038,11 @@ class InteractiveCleanUI:
1011
1038
  self._image_control_tab_groups = { }
1012
1039
 
1013
1040
  self._image_control_tab_groups[imid] = result
1041
+
1042
+ ### This set of tabs is the secondary, inner tabs for different controls for an individual image
1014
1043
  result.js_on_change( 'active', CustomJS( args=dict( ),
1015
- code='''document._casa_last_control_tab = cb_obj.active''' ) )
1044
+ code='''const appstate = Bokeh.find.appState(cb_obj)
1045
+ appstate.last_control_tab = cb_obj.active''' ) )
1016
1046
  return result
1017
1047
 
1018
1048
  def _create_image_panel( self, imagetuple ):
@@ -1279,20 +1309,22 @@ class InteractiveCleanUI:
1279
1309
 
1280
1310
  def _initialize_javascript( self ):
1281
1311
  self._js = { ### initialize state
1282
- ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1283
- ### -- document is used storing state --
1284
- ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1285
- 'initialize': '''if ( ! document._casa_initialized ) {
1312
+ ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1313
+ ### -- "logbutton" is a random GUI model which can be used to locate the --
1314
+ ### -- appstate that is used storing state --
1315
+ ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1316
+ 'initialize': '''const appstate = Bokeh.find.appState(logbutton)
1317
+ if ( ! appstate.ic_initialized ) {
1286
1318
  console.log(`casalib version: ${casalib.version}`)
1287
- document._casa_image_name = initial_image
1288
- document._casa_initialized = true
1289
- document._casa_window_closed = false
1319
+ appstate.image_name = initial_image
1320
+ appstate.ic_initialized = true
1321
+ appstate.window_closed = false
1290
1322
  window.addEventListener( 'beforeunload',
1291
1323
  function (e) {
1292
1324
  // if the window is already closed this message is never
1293
1325
  // delivered (unless interactive clean is called again then
1294
1326
  // the event shows up in the newly created control pipe)
1295
- if ( document._casa_window_closed == false ) {
1327
+ if ( appstate.window_closed == false ) {
1296
1328
  ctrl_pipe.send( ids['stop'],
1297
1329
  { action: 'stop', value: { } },
1298
1330
  undefined ) } } )
@@ -1368,13 +1400,13 @@ class InteractiveCleanUI:
1368
1400
 
1369
1401
  function update_convergence( recurse=false ) {
1370
1402
  let convdata
1371
- if ( hasprop(document,'_casa_convergence_data') ) {
1372
- convdata = document._casa_convergence_data
1403
+ if ( hasprop(appstate,'convergence_data') ) {
1404
+ convdata = appstate.convergence_data
1373
1405
  } else {
1374
1406
  if ( ! recurse ) {
1375
1407
  ctrl.converge.pipe.send( ctrl.converge.id, { action: 'retrieve' },
1376
1408
  (msg) => { if ( hasprop( msg.result, 'convergence' ) ) {
1377
- document._casa_convergence_data = { convergence: msg.result.convergence,
1409
+ appstate.convergence_data = { convergence: msg.result.convergence,
1378
1410
  cyclethreshold: msg.result.cyclethreshold }
1379
1411
  update_convergence(true)
1380
1412
  } } )
@@ -1409,8 +1441,8 @@ class InteractiveCleanUI:
1409
1441
  }
1410
1442
 
1411
1443
  if ( hasprop(clean_msg,'convergence') && clean_msg.convergence != null ) {
1412
- document._casa_convergence_data = { convergence: clean_msg.convergence,
1413
- cyclethreshold: clean_msg.cyclethreshold }
1444
+ appstate.convergence_data = { convergence: clean_msg.convergence,
1445
+ cyclethreshold: clean_msg.cyclethreshold }
1414
1446
  }
1415
1447
  }
1416
1448
 
@@ -1492,19 +1524,19 @@ class InteractiveCleanUI:
1492
1524
  'Stopping criteria encountered',
1493
1525
  'Unrecognized stop code' ]
1494
1526
  if ( typeof status === 'number' ) {
1495
- images_state[document._casa_image_name]['status'].text =
1527
+ images_state[appstate.image_name]['status'].text =
1496
1528
  '<p>' +
1497
1529
  stopstr[ status < 0 || status >= stopstr.length ?
1498
1530
  stopstr.length - 1 : status ] +
1499
1531
  '</p>'
1500
1532
  } else {
1501
- images_state[document._casa_image_name]['status'].text = `<p>${status}</p>`
1533
+ images_state[appstate.image_name]['status'].text = `<p>${status}</p>`
1502
1534
  }
1503
1535
  }''',
1504
1536
 
1505
1537
  'iter-gui-update': '''function get_update_dictionary( ) {
1506
- //const amste = images_state[document._casa_image_name]['automask']
1507
- //const clste = images_state[document._casa_image_name]['iteration']
1538
+ //const amste = images_state[appstate.image_name]['automask']
1539
+ //const clste = images_state[appstate.image_name]['iteration']
1508
1540
  // Assumption is that there is ONE set of iteration and automask updates
1509
1541
  // for ALL imaging fields...
1510
1542
  const amobj = Object.entries(images_state)[0][1].automask
@@ -1523,7 +1555,7 @@ class InteractiveCleanUI:
1523
1555
 
1524
1556
  const masks = Object.entries(images_state).reduce( (acc,[k,v]) => { acc[k] = v.src.masks( ); return acc }, { } )
1525
1557
  const breadcrumbs = Object.entries(images_state).reduce( (acc,[k,v]) => { acc[k] = v.src.breadcrumbs( ); return acc }, { } )
1526
- return { iteration, automask, masks, breadcrumbs, current_image: document._casa_image_name }
1558
+ return { iteration, automask, masks, breadcrumbs, current_image: appstate.image_name }
1527
1559
  }
1528
1560
  function update_log( log_lines ) {
1529
1561
  let b = logbutton
cubevis/utils/__init__.py CHANGED
@@ -67,6 +67,7 @@ from ._static import static_vars, static_dir
67
67
  from ._tiles import TMSTiles
68
68
  from ._contextmgrchain import ContextMgrChain
69
69
  from ._import_protected_module import ImportProtectedModule
70
+ from ._mutual_exclusion import MutualExclusionManager
70
71
 
71
72
  @static_vars(mgr=None)
72
73
  def resource_manager( ):