cubevis 1.0.14__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.
@@ -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, AppContext, RegionList
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 'document._cube_done( )'). It should then
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 = { 'sources': { } }
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
- if ( source && document._cube_done )
250
- document._cube_done(
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 document._cube_done callback ( `(msg) => { resolve(true); return false }` ) can
269
- ## return `false` (indicating that the window should not be closed by _cube_done)
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='''document._casa_last_control_tab = cb_obj.active''' ) )
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'] = column(
425
- row( self._fig['help'],
426
- Spacer( height=self._ctrl_state['stop'].height, sizing_mode="scale_width" ),
427
- Div( text="<div><b>status:</b></div>" ),
428
- self._fig['status'],
429
- self._ctrl_state['stop'], sizing_mode="scale_width" ),
430
- row( image_tabs, height_policy='max', width_policy='max' ),
431
- height_policy='max', width_policy='max' )
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 document._casa_image_name (which is
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='''if ( ! hasprop(document,'_casa_last_control_tab') ) {
445
- document._casa_last_control_tab = 0
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
- document._casa_image_name = names[cb_obj.active]
448
- itergroups[document._casa_image_name].active = document._casa_last_control_tab''' ) )
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
- show(self._fig['layout'])
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(bokeh_ui)
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(bokeh_ui)
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(bokeh_ui, startup, self.get_future, name="iclean-jpy")
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(bokeh_ui, startup, self.get_future, name="iclean-jpy")
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
@@ -28,6 +28,5 @@
28
28
  '''Common tools used in creating applications.'''
29
29
 
30
30
  from ._cube import CubeMask
31
- from ._app_context import AppContext
32
31
  from ._region_list import RegionList
33
32
  from ._interactive_clean_ui import InteractiveCleanUI
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
- document._cube_already_shutdown = false
1496
- ''' + self._js['mask-state-init'] +
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
- document._cube_done = ( final_polys=null, cb=null ) => {
1504
- if ( document._cube_already_shutdown ) return
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
- ("""console.log("Running in jupyter notebook. Not closing window.")""" if is_interactive_jupyter( ) else
1513
- """console.log("Running from script/terminal. Closing window.")
1514
- window.close()"""
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
- document._cube_already_shutdown = true
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 ( ! ('_masking_state' in document) ) {
1530
- document._masking_state = { }
1531
- }
1532
- document._masking_state[source.id] = false
1533
- source.masking_on = ( ) => { return document._masking_state[source.id] }
1534
- source.disable_masking = ( ) => { source._masking_enabled = false; document._masking_state[source.id] = false }
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 document._polygon_list == 'undefined' )
2659
- document._polygon_list = [ ]
2675
+ if ( typeof appstate.polygon_list == 'undefined' )
2676
+ appstate.polygon_list = [ ]
2660
2677
 
2661
- const polys = document._polygon_list
2678
+ const polys = appstate.polygon_list
2662
2679
 
2663
2680
  return {
2664
2681
  reset_annos: ( ) => {