cubevis 0.5.23__py3-none-any.whl → 0.5.25__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.

Potentially problematic release.


This version of cubevis might be problematic. Click here for more details.

@@ -44,12 +44,13 @@ import asyncio
44
44
  import shutil
45
45
  import websockets
46
46
  from os.path import basename, abspath, exists, join
47
+ import numpy as np
47
48
  from uuid import uuid4
48
49
  from html import escape as html_escape
49
50
  from contextlib import asynccontextmanager
50
51
  from bokeh.models import Button, TextInput, Checkbox, Div, LinearAxis, CustomJS, Spacer, Span, HoverTool, DataRange1d, Step, InlineStyleSheet
51
52
  from bokeh.events import ModelEvent, MouseEnter
52
- from bokeh.models import TabPanel, Tabs
53
+ from bokeh.models import TabPanel, Tabs, Range1d
53
54
  from bokeh.plotting import ColumnDataSource, figure, show
54
55
  from bokeh.layouts import column, row, layout
55
56
  from bokeh.io import reset_output as reset_bokeh_output, output_notebook
@@ -277,6 +278,23 @@ class InteractiveCleanUI:
277
278
  imdetails['path']['residual'] = join( output_dir, self._clean['gclean_paths'][imid]['residualname'] )
278
279
  imdetails['path']['mask'] = join( output_dir, self._clean['gclean_paths'][imid]['maskname'] )
279
280
 
281
+ ###
282
+ ### There is one set of tclean controls for all images/outlier/etc. because
283
+ ### in the final version gclean will handle the iterations for all fields...
284
+ ###
285
+ cwidth = 64
286
+ cheight = 40
287
+ self._control['iteration'] = { }
288
+ self._control['iteration']['continue'] = TipButton( max_width=cwidth, max_height=cheight, name='continue',
289
+ icon=svg_icon(icon_name="iclean-continue", size=18),
290
+ tooltip=Tooltip( content=HTML( '''Stop after <b>one major cycle</b> or when any stopping criteria is met.''' ), position='left') )
291
+ self._control['iteration']['finish'] = TipButton( max_width=cwidth, max_height=cheight, name='finish',
292
+ icon=svg_icon(icon_name="iclean-finish", size=18),
293
+ tooltip=Tooltip( content=HTML( '''<b>Continue</b> until some stopping criteria is met.''' ), position='left') )
294
+ self._control['iteration']['stop'] = TipButton( button_type="danger", max_width=cwidth, max_height=cheight, name='stop',
295
+ icon=svg_icon(icon_name="iclean-stop", size=18),
296
+ tooltip=Tooltip( content=HTML( '''<p>Clicking a <font color="red">red</font> stop button will cause this tab to close and control will return to Python.<p>Clicking an <font color="orange">orange</font> stop button will cause <tt>tclean</tt> to stop after the current major cycle.''' ), position='left' ) )
297
+
280
298
  for idx, (imid, imdetails) in enumerate(self._clean_targets.items( )):
281
299
  imdetails['gui'] = { }
282
300
 
@@ -289,8 +307,13 @@ class InteractiveCleanUI:
289
307
  ###
290
308
  imdetails['gui']['cube'] = CubeMask( imdetails['path']['residual'], mask=imdetails['path']['mask'], abort=self._abort_handler,
291
309
  init_script=CustomJS( args=dict( initial_convergence_state=self._init_values["convergence_state"],
310
+ clean_ctrl=self._control['iteration'],
292
311
  name=imid ),
293
- code='''document._casa_convergence_data = initial_convergence_state''' )
312
+ code='''document._casa_convergence_data = initial_convergence_state
313
+ clean_ctrl.continue.disable_add_sub = this.disable_add_sub.values
314
+ clean_ctrl.finish.disable_add_sub = this.disable_add_sub.values
315
+ clean_ctrl.stop.disable_add_sub = this.disable_add_sub.values
316
+ this.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
294
317
  if idx == 0 else None )
295
318
 
296
319
  ###
@@ -342,57 +365,159 @@ class InteractiveCleanUI:
342
365
  </div>'''
343
366
 
344
367
  hover = HoverTool( tooltips=TOOLTIPS )
345
- imdetails['gui']['convergence'] = figure( sizing_mode=sizing_mode, y_axis_location="right",
346
- tools=[ hover ], toolbar_location=None, **kw )
347
368
 
348
- if orient == 'vertical':
349
- imdetails['gui']['convergence'].yaxis.axis_label='Iteration (cycle threshold dotted red)'
350
- imdetails['gui']['convergence'].xaxis.axis_label='Peak Residual'
351
- imdetails['gui']['convergence'].extra_x_ranges = { 'residual_range': DataRange1d( follow='end' ),
352
- 'flux_range': DataRange1d( follow='end' ) }
353
-
354
- imdetails['gui']['convergence'].step( 'values', 'iterations', source=imdetails['converge-data']['cyclethreshold'],
355
- line_color='red', x_range_name='residual_range', line_dash='dotted', line_width=2 )
356
- imdetails['gui']['convergence'].line( 'values', 'iterations', source=imdetails['converge-data']['residual'],
357
- line_color=self._converge_color['residual'], x_range_name='residual_range' )
358
- imdetails['gui']['convergence'].scatter( 'values', 'iterations', source=imdetails['converge-data']['residual'],
359
- color=self._converge_color['residual'], x_range_name='residual_range',size=10 )
360
- imdetails['gui']['convergence'].line( 'values', 'iterations', source=imdetails['converge-data']['flux'],
361
- line_color=self._converge_color['flux'], x_range_name='flux_range' )
362
- imdetails['gui']['convergence'].scatter( 'values', 'iterations', source=imdetails['converge-data']['flux'],
363
- color=self._converge_color['flux'], x_range_name='flux_range', size=10 )
364
-
365
- imdetails['gui']['convergence'].add_layout( LinearAxis( x_range_name='flux_range', axis_label='Total Flux',
366
- axis_line_color=self._converge_color['flux'],
367
- major_label_text_color=self._converge_color['flux'],
368
- axis_label_text_color=self._converge_color['flux'],
369
- major_tick_line_color=self._converge_color['flux'],
370
- minor_tick_line_color=self._converge_color['flux'] ), 'above')
369
+ ###
370
+ ### Data source that will be used for updating the convergence plot
371
+ ###
372
+ stokes = 0
373
+ convergence = imdetails['converge']['chan'][0][stokes]
374
+ imdetails['converge-data'] = { }
375
+ imdetails['converge-data']['flux'] = ColumnDataSource( data=dict( values=convergence['modelFlux'], iterations=convergence['iterations'],
376
+ cyclethreshold=convergence['cycleThresh'],
377
+ stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
378
+ type=['flux'] * len(convergence['iterations']) ) )
379
+ imdetails['converge-data']['residual'] = ColumnDataSource( data=dict( values=convergence['peakRes'], iterations=convergence['iterations'],
380
+ cyclethreshold=convergence['cycleThresh'],
381
+ stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
382
+ type=['residual'] * len(convergence['iterations'])) )
383
+ imdetails['converge-data']['cyclethreshold'] = ColumnDataSource( data=dict( values=convergence['cycleThresh'], iterations=convergence['iterations'] ) )
384
+
385
+ # Calculate explicit ranges for each dataset
386
+ flux_values = convergence['modelFlux']
387
+ residual_values = convergence['peakRes']
388
+ iterations = convergence['iterations']
389
+ cyclethresh_values = convergence['cycleThresh']
390
+
391
+ # Calculate ranges with padding
392
+ if len(flux_values) > 0:
393
+ flux_min, flux_max = np.min(flux_values), np.max(flux_values)
394
+ flux_padding = max((flux_max - flux_min) * 0.1, abs(flux_max * 0.05)) if flux_max != flux_min else abs(flux_max * 0.1)
395
+ else:
396
+ flux_min, flux_max, flux_padding = 0, 1, 0.1
371
397
 
398
+ if len(residual_values) > 0:
399
+ residual_min, residual_max = np.min(residual_values), np.max(residual_values)
400
+ residual_padding = max((residual_max - residual_min) * 0.1, abs(residual_max * 0.05)) if residual_max != residual_min else abs(residual_max * 0.1)
372
401
  else:
373
- imdetails['gui']['convergence'].xaxis.axis_label='Iteration (cycle threshold dotted red)'
374
- imdetails['gui']['convergence'].yaxis.axis_label='Peak Residual'
375
- imdetails['gui']['convergence'].extra_y_ranges = { 'residual_range': DataRange1d( follow='end' ),
376
- 'flux_range': DataRange1d( follow='end' ) }
377
-
378
- imdetails['gui']['convergence'].step( 'iterations', 'values', source=imdetails['converge-data']['cyclethreshold'],
379
- line_color='red', y_range_name='residual_range', line_dash='dotted', line_width=2 )
380
- imdetails['gui']['convergence'].line( 'iterations', 'values', source=imdetails['converge-data']['residual'],
381
- line_color=self._converge_color['residual'], y_range_name='residual_range' )
382
- imdetails['gui']['convergence'].scatter( 'iterations', 'values', source=imdetails['converge-data']['residual'],
383
- color=self._converge_color['residual'], y_range_name='residual_range',size=10 )
384
- imdetails['gui']['convergence'].line( 'iterations', 'values', source=imdetails['converge-data']['flux'],
385
- line_color=self._converge_color['flux'], y_range_name='flux_range' )
386
- imdetails['gui']['convergence'].scatter( 'iterations', 'values', source=imdetails['converge-data']['flux'],
387
- color=self._converge_color['flux'], y_range_name='flux_range', size=10 )
388
-
389
- imdetails['gui']['convergence'].add_layout( LinearAxis( y_range_name='flux_range', axis_label='Total Flux',
390
- axis_line_color=self._converge_color['flux'],
391
- major_label_text_color=self._converge_color['flux'],
392
- axis_label_text_color=self._converge_color['flux'],
393
- major_tick_line_color=self._converge_color['flux'],
394
- minor_tick_line_color=self._converge_color['flux'] ), 'right')
402
+ residual_min, residual_max, residual_padding = 0, 1, 0.1
403
+
404
+ # Create Range1d objects
405
+ flux_range = Range1d(start=flux_min - flux_padding, end=flux_max + flux_padding)
406
+ residual_range = Range1d(start=residual_min - residual_padding, end=residual_max + residual_padding)
407
+
408
+ # Ensure ranges are valid (non-zero span)
409
+ if flux_range.end - flux_range.start < 1e-10:
410
+ center = flux_range.start
411
+ if abs(center) < 1e-10: # If center is essentially 0
412
+ flux_range.start = -0.1
413
+ flux_range.end = 1.0
414
+ else:
415
+ span = max(abs(center * 0.2), 0.1)
416
+ flux_range.start = center - span
417
+ flux_range.end = center + span
418
+
419
+ if residual_range.end - residual_range.start < 1e-10:
420
+ center = residual_range.start
421
+ span = max(abs(center * 0.2), 0.1)
422
+ residual_range.start = center - span
423
+ residual_range.end = center + span
424
+
425
+ # ORIENTATION CONFIGURATION - this eliminates the conditional branches
426
+ config = {
427
+ 'vertical': {
428
+ 'iteration_axis': 'y',
429
+ 'data_axis': 'x',
430
+ 'main_axis_label': 'Iteration (cycle threshold dotted red)',
431
+ 'residual_axis_label': 'Peak Residual',
432
+ 'flux_axis_label': 'Total Flux',
433
+ 'residual_axis_pos': 'above',
434
+ 'flux_axis_pos': 'above',
435
+ 'extra_ranges_key': 'extra_x_ranges',
436
+ 'glyph_coords': ('values', 'iterations'), # (x, y)
437
+ 'range_name_param': 'x_range_name'
438
+ },
439
+ 'horizontal': {
440
+ 'iteration_axis': 'x',
441
+ 'data_axis': 'y',
442
+ 'main_axis_label': 'Iteration (cycle threshold dotted red)',
443
+ 'residual_axis_label': 'Peak Residual',
444
+ 'flux_axis_label': 'Total Flux',
445
+ 'residual_axis_pos': 'right',
446
+ 'flux_axis_pos': 'right',
447
+ 'extra_ranges_key': 'extra_y_ranges',
448
+ 'glyph_coords': ('iterations', 'values'), # (x, y)
449
+ 'range_name_param': 'y_range_name'
450
+ }
451
+ }
395
452
 
453
+ cfg = config[orient]
454
+
455
+ # Create figure with no default axes; to control the axes they must
456
+ # be explicitly set to None
457
+ imdetails['gui']['convergence'] = figure( sizing_mode=sizing_mode,
458
+ x_axis_location=None, y_axis_location=None,
459
+ tools=[ hover ], toolbar_location=None, **kw )
460
+
461
+ # Set up extra ranges
462
+ setattr(imdetails['gui']['convergence'], cfg['extra_ranges_key'], {
463
+ 'residual_range': residual_range,
464
+ 'flux_range': flux_range
465
+ })
466
+
467
+ # Store references for JavaScript updates
468
+ imdetails['converge-ranges'] = {
469
+ 'residual_range': residual_range,
470
+ 'flux_range': flux_range
471
+ }
472
+
473
+ # Create main iteration axis
474
+ main_axis = LinearAxis(axis_label=cfg['main_axis_label'])
475
+ main_axis_pos = 'below' if cfg['iteration_axis'] == 'x' else 'left'
476
+ imdetails['gui']['convergence'].add_layout(main_axis, main_axis_pos)
477
+
478
+ # Create glyphs using configuration
479
+ x_coord, y_coord = cfg['glyph_coords']
480
+ range_param = {cfg['range_name_param']: 'residual_range'}
481
+
482
+ imdetails['gui']['convergence'].step( x_coord, y_coord, source=imdetails['converge-data']['cyclethreshold'],
483
+ line_color='red', line_dash='dotted', line_width=2, **range_param )
484
+ imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['residual'],
485
+ line_color=self._converge_color['residual'], **range_param )
486
+ imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['residual'],
487
+ color=self._converge_color['residual'], size=10, **range_param )
488
+
489
+ # Flux glyphs
490
+ range_param = {cfg['range_name_param']: 'flux_range'}
491
+ imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['flux'],
492
+ line_color=self._converge_color['flux'], **range_param )
493
+ imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['flux'],
494
+ color=self._converge_color['flux'], size=10, **range_param )
495
+
496
+ # Create and style residual axis
497
+ residual_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'residual_range'}
498
+ residual_axis = LinearAxis(axis_label=cfg['residual_axis_label'], **residual_axis_param)
499
+ residual_axis.axis_line_color = self._converge_color['residual']
500
+ residual_axis.major_label_text_color = self._converge_color['residual']
501
+ residual_axis.axis_label_text_color = self._converge_color['residual']
502
+ residual_axis.major_tick_line_color = self._converge_color['residual']
503
+ residual_axis.minor_tick_line_color = self._converge_color['residual']
504
+ imdetails['gui']['convergence'].add_layout(residual_axis, cfg['residual_axis_pos'])
505
+
506
+ # Create and style flux axis
507
+ flux_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'flux_range'}
508
+ flux_axis = LinearAxis(axis_label=cfg['flux_axis_label'], **flux_axis_param)
509
+ flux_axis.axis_line_color = self._converge_color['flux']
510
+ flux_axis.major_label_text_color = self._converge_color['flux']
511
+ flux_axis.axis_label_text_color = self._converge_color['flux']
512
+ flux_axis.major_tick_line_color = self._converge_color['flux']
513
+ flux_axis.minor_tick_line_color = self._converge_color['flux']
514
+ imdetails['gui']['convergence'].add_layout(flux_axis, cfg['flux_axis_pos'])
515
+
516
+ # Store axis references for JavaScript access
517
+ imdetails['converge-axes'] = {
518
+ 'residual_axis': residual_axis,
519
+ 'flux_axis': flux_axis
520
+ }
396
521
 
397
522
  def _launch_gui( self ):
398
523
  '''create and show GUI
@@ -484,24 +609,6 @@ class InteractiveCleanUI:
484
609
  #print("%s: %s" % ( btn, self._clean_ids[btn] ) )
485
610
  self._pipe['control'].register( self._clean_ids[btn], clean_handler )
486
611
 
487
-
488
- ###
489
- ### There is one set of tclean controls for all images/outlier/etc. because
490
- ### in the final version gclean will handle the iterations for all fields...
491
- ###
492
- cwidth = 64
493
- cheight = 40
494
- self._control['iteration'] = { }
495
- self._control['iteration']['continue'] = TipButton( max_width=cwidth, max_height=cheight, name='continue',
496
- icon=svg_icon(icon_name="iclean-continue", size=18),
497
- tooltip=Tooltip( content=HTML( '''Stop after <b>one major cycle</b> or when any stopping criteria is met.''' ), position='left') )
498
- self._control['iteration']['finish'] = TipButton( max_width=cwidth, max_height=cheight, name='finish',
499
- icon=svg_icon(icon_name="iclean-finish", size=18),
500
- tooltip=Tooltip( content=HTML( '''<b>Continue</b> until some stopping criteria is met.''' ), position='left') )
501
- self._control['iteration']['stop'] = TipButton( button_type="danger", max_width=cwidth, max_height=cheight, name='stop',
502
- icon=svg_icon(icon_name="iclean-stop", size=18),
503
- tooltip=Tooltip( content=HTML( '''<p>Clicking a <font color="red">red</font> stop button will cause this tab to close and control will return to Python.<p>Clicking an <font color="orange">orange</font> stop button will cause <tt>tclean</tt> to stop after the current major cycle.''' ), position='left' ) )
504
-
505
612
  ###
506
613
  ### The single SHARED help button will be supplied by the first CubeMask...
507
614
  ###
@@ -533,23 +640,6 @@ class InteractiveCleanUI:
533
640
 
534
641
  self._clean['converge']['pipe'].register( self._clean['converge']['id'], convergence_handler )
535
642
 
536
- ###
537
- ### Data source that will be used for updating the convergence plot
538
- ###
539
- stokes = 0
540
- convergence = imdetails['converge']['chan'][0][stokes]
541
- imdetails['converge-data'] = { }
542
- imdetails['converge-data']['flux'] = ColumnDataSource( data=dict( values=convergence['modelFlux'], iterations=convergence['iterations'],
543
- cyclethreshold=convergence['cycleThresh'],
544
- stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
545
- type=['flux'] * len(convergence['iterations']) ) )
546
- imdetails['converge-data']['residual'] = ColumnDataSource( data=dict( values=convergence['peakRes'], iterations=convergence['iterations'],
547
- cyclethreshold=convergence['cycleThresh'],
548
- stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
549
- type=['residual'] * len(convergence['iterations'])) )
550
- imdetails['converge-data']['cyclethreshold'] = ColumnDataSource( data=dict( values=convergence['cycleThresh'], iterations=convergence['iterations'] ) )
551
-
552
-
553
643
  ###
554
644
  ### help page for cube interactions
555
645
  ###
@@ -584,8 +674,12 @@ class InteractiveCleanUI:
584
674
  imdetails['gui']['image']['src'] = imdetails['gui']['cube'].js_obj( )
585
675
  imdetails['gui']['image']['fig'] = imdetails['gui']['cube'].image( grid=False, height_policy='max', width_policy='max',
586
676
  channelcb=CustomJS( args=dict( img_state={ 'src': imdetails['gui']['image']['src'],
587
- 'flux': imdetails['converge-data']['flux'],
588
- 'residual': imdetails['converge-data']['residual'],
677
+ 'flux': { 'source': imdetails['converge-data']['flux'],
678
+ 'axis': imdetails['converge-axes']['flux_axis'],
679
+ 'range': imdetails['converge-ranges']['flux_range'] },
680
+ 'residual': { 'source': imdetails['converge-data']['residual'],
681
+ 'axis': imdetails['converge-axes']['residual_axis'],
682
+ 'range': imdetails['converge-ranges']['residual_range'] },
589
683
  'cyclethreshold': imdetails['converge-data']['cyclethreshold'] },
590
684
  imid=imid,
591
685
  ctrl={ 'converge': self._clean['converge'] },
@@ -694,6 +788,7 @@ class InteractiveCleanUI:
694
788
  margin=(-1, 0, -10, 0), button_type='light',
695
789
  stylesheets=[ InlineStyleSheet( css='''.bk-btn { border: 0px solid #ccc; padding: 0 var(--padding-vertical) var(--padding-horizontal); margin-top: 3px; }''' ) ] )
696
790
 
791
+
697
792
  self._control['iteration']['cb'] = CustomJS( args=dict( images_state={ k: { 'status': v['gui']['stopcode'],
698
793
  'automask': v['gui']['params']['automask'],
699
794
  'iteration': v['gui']['params']['iteration'],
@@ -701,9 +796,13 @@ class InteractiveCleanUI:
701
796
  'src': v['gui']['cube'].js_obj( ),
702
797
  'spectrum': v['gui']['spectrum'],
703
798
  'src': v['gui']['image']['src'],
704
- 'flux': v['converge-data']['flux'],
799
+ 'flux': { 'source': v['converge-data']['flux'],
800
+ 'axis': v['converge-axes']['flux_axis'],
801
+ 'range': v['converge-ranges']['flux_range'] },
705
802
  'cyclethreshold': v['converge-data']['cyclethreshold'],
706
- 'residual': v['converge-data']['residual'],
803
+ 'residual': { 'source': v['converge-data']['residual'],
804
+ 'axis': v['converge-axes']['residual_axis'],
805
+ 'range': v['converge-ranges']['residual_range'] },
707
806
  'navi': { 'slider': v['gui']['slider'],
708
807
  'goto': v['gui']['goto'],
709
808
  ## it doesn't seem like pixel tracking must be disabled
@@ -1215,7 +1314,52 @@ class InteractiveCleanUI:
1215
1314
  ### -- The "Insert here ..." code seems to be called when when the stokes plane is changed --
1216
1315
  ### -- but there have been no tclean iterations yet... --
1217
1316
  ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1218
- 'update-converge': '''function update_convergence_single( target, data ) {
1317
+ 'update-converge': '''function updateAxisRange(source, range_obj, axis_obj, axis_name) {
1318
+ const data = source.data;
1319
+ const values = data['values'];
1320
+
1321
+ if (values.length === 0) return;
1322
+
1323
+ // Calculate data bounds
1324
+ const min_val = Math.min(...values);
1325
+ const max_val = Math.max(...values);
1326
+
1327
+ // Add padding (10% of range, with minimum padding)
1328
+ const span = max_val - min_val;
1329
+ const padding = max_val != min_val ? Math.max(span * 0.1, Math.abs(max_val * 0.02)) : Math.abs(max_val * 0.1);
1330
+
1331
+ // Update range
1332
+ range_obj.start = min_val - padding;
1333
+ range_obj.end = max_val + padding;
1334
+
1335
+ // Ensure ranges are valid (non-zero span)
1336
+ if ( range_obj.end - range_obj.start < 1e-10 ) {
1337
+ const center = range_obj.start
1338
+ if ( Math.abs(center) < 1e-10 ) {
1339
+ // If center is essentially 0
1340
+ range_obj.start = -0.1
1341
+ range_obj.end = 1.0
1342
+ } else {
1343
+ const span = Math.max(Math.abs(center * 0.2), 0.1)
1344
+ range_obj.start = center - span
1345
+ range_obj.end = center + span
1346
+ }
1347
+ }
1348
+
1349
+ // Optional: Update tick density
1350
+ if (axis_obj && axis_obj.ticker) {
1351
+ const new_span = range_obj.end - range_obj.start;
1352
+ if (new_span < 10) {
1353
+ axis_obj.ticker.desired_num_ticks = 8;
1354
+ } else if (new_span < 100) {
1355
+ axis_obj.ticker.desired_num_ticks = 10;
1356
+ } else {
1357
+ axis_obj.ticker.desired_num_ticks = 6;
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ function update_convergence_single( target, data ) {
1219
1363
  const pos = target.src.cur_chan
1220
1364
  const imdata = data.get(pos[1]).get(pos[0])
1221
1365
  // chan----------------^^^^^^ ^^^^^^----stokes
@@ -1225,9 +1369,11 @@ class InteractiveCleanUI:
1225
1369
  const modelFlux = imdata.modelFlux
1226
1370
  const stopCode = imdata.stopCode
1227
1371
  const stopDesc = imdata.stopCode.map( code => stopdescmap.has(code) ? stopdescmap.get(code): "" )
1228
- target.residual.data = { iterations, cyclethreshold, stopDesc, values: peakRes, type: Array(iterations.length).fill('residual') }
1229
- target.flux.data = { iterations, cyclethreshold, stopDesc, values: modelFlux, type: Array(iterations.length).fill('flux') }
1372
+ target.residual.source.data = { iterations, cyclethreshold, stopDesc, values: peakRes, type: Array(iterations.length).fill('residual') }
1373
+ target.flux.source.data = { iterations, cyclethreshold, stopDesc, values: modelFlux, type: Array(iterations.length).fill('flux') }
1230
1374
  target.cyclethreshold.data = { iterations, values: cyclethreshold }
1375
+ updateAxisRange( target.flux.source, target.flux.range, target.flux.axis, 'Flux' )
1376
+ updateAxisRange( target.residual.source, target.residual.range, target.residual.axis, 'Residual' )
1231
1377
  }
1232
1378
 
1233
1379
  function update_convergence( recurse=false ) {
@@ -1311,6 +1457,7 @@ class InteractiveCleanUI:
1311
1457
  )
1312
1458
  }
1313
1459
  )
1460
+ clean_ctrl.continue.disable_add_sub.disabled = true
1314
1461
  clean_ctrl.continue.disabled = true
1315
1462
  clean_ctrl.finish.disabled = true
1316
1463
  clean_ctrl.stop.disabled = with_stop
@@ -1335,6 +1482,7 @@ class InteractiveCleanUI:
1335
1482
 
1336
1483
  clean_ctrl.stop.disabled = false
1337
1484
  if ( ! only_stop ) {
1485
+ clean_ctrl.continue.disable_add_sub.disabled = false
1338
1486
  clean_ctrl.continue.disabled = false
1339
1487
  clean_ctrl.finish.disabled = false
1340
1488
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cubevis
3
- Version: 0.5.23
3
+ Version: 0.5.25
4
4
  Summary: visualization toolkit and apps for casa
5
5
  License: LGPL
6
6
  Author-email: Darrell Schiebel <darrell@schiebel.us>,Pam Harris <pharris@nrao.edu>
@@ -29,7 +29,7 @@ cubevis/__js__/bokeh-3.6.1.min.js,sha256=SPRs94Q-H-aj8MCsXNu4ok1ouQQLTgXxZnk0-BB
29
29
  cubevis/__js__/bokeh-tables-3.6.1.min.js,sha256=wINufoBiINmP_PERwhN_1GkidJOsJQ_3vFKUDui7rl8,301216
30
30
  cubevis/__js__/bokeh-widgets-3.6.1.min.js,sha256=NE3tFbbxoaMjnJ0XednWJxbAGl-vSR0fxE_kX8keuDQ,311821
31
31
  cubevis/__js__/casalib.min.js,sha256=JLZ_3i5JlbNJw2nsx7pewysxzoD3sVpSiWdgJCLbhi0,91107
32
- cubevis/__js__/cubevisjs.min.js,sha256=fU2O_iQAIKl79r8rfADaTq5R-tMy33YqimE3U-UHXGE,27545
32
+ cubevis/__js__/cubevisjs.min.js,sha256=60KOq5Nt2OW_eGuJ_BcI-GndTEQAL4gZNkbZE9Lve0M,28238
33
33
  cubevis/bokeh/__init__.py,sha256=XvuKcU9-bAv1CPb_O81VJTNLlHQC-zBg_Ig9_q4RkM4,1371
34
34
  cubevis/bokeh/annotations/__init__.py,sha256=tjDIPKbg-rh7Iu3coFWvmX-j2yNj9KuKmRp1aTo71ww,50
35
35
  cubevis/bokeh/annotations/_ev_poly_annotation.py,sha256=0ayX21gxNnm5-4s5VRKiaJ23DCSvbvpsU6oym9q2bk0,173
@@ -37,9 +37,10 @@ cubevis/bokeh/components/__init__.py,sha256=BQOqgBtlDpu6ENrY42nYeS73n2ZSMogJgM2L
37
37
  cubevis/bokeh/format/__init__.py,sha256=umNotXSRR23675c44x3h5h2ILbZX8xrDFCztvriYjEw,1424
38
38
  cubevis/bokeh/format/_time_ticks.py,sha256=j-DcPh7RfGE8iX2bPjLQDQPIbiAbmjiEWQnKmdMWA3I,1773
39
39
  cubevis/bokeh/format/_wcs_ticks.py,sha256=x-ObJiCxBvfqCEk8ySh0NU1ZQUPFUdHRHztjwPSPECI,1827
40
- cubevis/bokeh/models/__init__.py,sha256=V6KjvlK24elu-Sij7nQkpwc0itgDBR2o18EODXNbGQI,130
40
+ cubevis/bokeh/models/__init__.py,sha256=rlZbGqSv0nmg5At9fZDofK3tF8PLM6sp7hpE3Bgi6Wg,167
41
41
  cubevis/bokeh/models/_edit_span.py,sha256=7o59ZS0bF_Q_WtstvViWXP-2PiY_F7_zCecTqKcmz0E,196
42
42
  cubevis/bokeh/models/_ev_text_input.py,sha256=SGtefXkWK6jHEk4EneZ_hEUGwoIWwVGjwqLjGsAXMpY,158
43
+ cubevis/bokeh/models/_shared_dict.py,sha256=AWjLMOKVAXWHSF4gcK2RbxF442QlzQ7hMR0oaODCqB0,901
43
44
  cubevis/bokeh/models/_tip.py,sha256=bnlCOYXsNdgEYKDbsQ6hEVrKOjCXEDxtuwE3sldOeEU,1396
44
45
  cubevis/bokeh/models/_tip_button.py,sha256=Dw4aO37o0J3n6EoaJ3ui1y8d04qNn3x2dbKiwiQpArM,1577
45
46
  cubevis/bokeh/sources/__init__.py,sha256=4FsudFuVU4o7VG5OG3K1tiMoxIXcJWNz_K9yzMDE8ls,1581
@@ -95,9 +96,9 @@ cubevis/remote/_local.py,sha256=PcPCFcwttTFZd3O33-5pqDuGKQKK6CA0gz1MTIkTiNI,1032
95
96
  cubevis/remote/_remote_kernel.py,sha256=wfu7ZzKn-oCxZxzDIkC5puBvGf8WbCLYL3CzM56_FNc,2652
96
97
  cubevis/toolbox/__init__.py,sha256=xzqwAG9863d7UKBVBRw7FrRUQbvCdFcLBq4vTpg63DU,1487
97
98
  cubevis/toolbox/_app_context.py,sha256=0tRY2SSbSCM6RKLFs_T707_ehWkJXPvnLlE1P9cLXJY,3024
98
- cubevis/toolbox/_cube.py,sha256=T-9I4f_rpiZRarT6rOzCwgveQCnWp7sV1_jNf6OX0lU,294698
99
- cubevis/toolbox/_interactive_clean_ui.mustache,sha256=DKmy6dMpGOq4aZImorhF0K2wOV0oi58qkv7o4CdDJEM,103985
100
- cubevis/toolbox/_interactive_clean_ui.py,sha256=I9ikjAJjN64gxql_CXoiUCg5wTclrV1pENGbDVczFic,103896
99
+ cubevis/toolbox/_cube.py,sha256=Jjf40aahUo6ZT9KP8b1RUbBtdd1CSLtE48RQF6q4EkQ,298496
100
+ cubevis/toolbox/_interactive_clean_ui.mustache,sha256=EyihZ-ZNe1hMxdQfkH2lPEo_Km-Kb7ZVcDF3VBzuxNE,111910
101
+ cubevis/toolbox/_interactive_clean_ui.py,sha256=FJdGxbdNHOVlSy0PZULQAUXwdVdAHBEpLGOTe-_0lHs,111821
101
102
  cubevis/toolbox/_interactiveclean_wrappers.py,sha256=XqyCGz33CMDhszTxnwZ_3-64GszUK1XYnGKUOxl9sas,5071
102
103
  cubevis/toolbox/_region_list.py,sha256=_1RvnXwqMoaAq8CPy-6IyhabLi_snXqO566onehI2y0,8957
103
104
  cubevis/utils/_ResourceManager.py,sha256=SaaR29etabRiKxmUK-aWvAm4v_OPFJH8CX7bNFm0Lgo,3410
@@ -114,8 +115,8 @@ cubevis/utils/_pkgs.py,sha256=mu2CCzndmJZYP81UkFhxveW_CisWLUvagJVolHOEVgM,2294
114
115
  cubevis/utils/_regions.py,sha256=TdAg4ZUUyhg3nFmX9_KLboqmc0LkyOdEW8M1WDR5Udk,1669
115
116
  cubevis/utils/_static.py,sha256=rN-sqXNqQ5R2M3wmPHU1GPP5OTyyWQlUPRuimCrht-g,2347
116
117
  cubevis/utils/_tiles.py,sha256=A9W1X61VOhBMTOKXVajzOIoiV2FBdO5N2SFB9SUpDOo,7336
117
- cubevis/__version__.py,sha256=I49ytBecDTKF_6KFJBcXA0zcgUvvXhff2l52LZ7lz8M,22
118
- cubevis-0.5.23.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
119
- cubevis-0.5.23.dist-info/METADATA,sha256=X0m2sjmGhdkEO7wR87wpOudzfiMzU8EjyLTIoq9Rd7w,2629
120
- cubevis-0.5.23.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
121
- cubevis-0.5.23.dist-info/RECORD,,
118
+ cubevis/__version__.py,sha256=kSyf03ZyJGgJcFLGc0Q1b3QZ1UfFrJEhukKkhdMWiAM,22
119
+ cubevis-0.5.25.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
120
+ cubevis-0.5.25.dist-info/METADATA,sha256=_5negZulwCmL_4YPAbnW31Bod3XBEgsg3cv2xxPKmA8,2629
121
+ cubevis-0.5.25.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
122
+ cubevis-0.5.25.dist-info/RECORD,,