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
|
@@ -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
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
clean_ctrl.
|
|
308
|
-
|
|
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,
|
|
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=
|
|
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
|
-
|
|
880
|
+
appstate.window_closed = true
|
|
862
881
|
/*** this will close the tab >>>>---------+ ***/
|
|
863
882
|
/*** | ***/
|
|
864
|
-
/***
|
|
865
|
-
|
|
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'] =
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
|
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='''
|
|
957
|
-
|
|
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
|
-
|
|
960
|
-
itergroups[
|
|
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='''
|
|
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
|
-
### --
|
|
1285
|
-
###
|
|
1286
|
-
|
|
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
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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 (
|
|
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(
|
|
1373
|
-
convdata =
|
|
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
|
-
|
|
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
|
-
|
|
1414
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
1508
|
-
//const clste = images_state[
|
|
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:
|
|
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
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
clean_ctrl.
|
|
307
|
-
|
|
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,
|
|
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=
|
|
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
|
-
|
|
879
|
+
appstate.window_closed = true
|
|
861
880
|
/*** this will close the tab >>>>---------+ ***/
|
|
862
881
|
/*** | ***/
|
|
863
|
-
/***
|
|
864
|
-
|
|
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'] =
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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
|
|
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='''
|
|
956
|
-
|
|
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
|
-
|
|
959
|
-
itergroups[
|
|
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='''
|
|
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
|
-
### --
|
|
1284
|
-
###
|
|
1285
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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 (
|
|
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(
|
|
1372
|
-
convdata =
|
|
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
|
-
|
|
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
|
-
|
|
1413
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
1507
|
-
//const clste = images_state[
|
|
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:
|
|
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( ):
|