Skip to content

Commit 4302868

Browse files
committed
Fix tab setup to maintain current tab state after applying parameters; add tests to verify single tab presence for Creation and Processing tabs
1 parent 32b0b63 commit 4302868

2 files changed

Lines changed: 119 additions & 5 deletions

File tree

datalab/gui/panel/base.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -456,11 +456,14 @@ def _values_equal(val1: Any, val2: Any) -> bool:
456456
# Handle regular comparison
457457
return val1 == val2
458458

459-
def setup_creation_tab(self, obj: SignalObj | ImageObj) -> bool:
459+
def setup_creation_tab(
460+
self, obj: SignalObj | ImageObj, set_current: bool = False
461+
) -> bool:
460462
"""Setup the Creation tab with parameter editor for interactive object creation.
461463
462464
Args:
463465
obj: Signal or Image object
466+
set_current: If True, set the Creation tab as current after creation
464467
465468
Returns:
466469
True if Creation tab was set up, False otherwise
@@ -495,6 +498,11 @@ def setup_creation_tab(self, obj: SignalObj | ImageObj) -> bool:
495498
self.creation_scroll.setWidgetResizable(True)
496499
self.creation_scroll.setWidget(editor)
497500
self.insertTab(0, self.creation_scroll, _("Creation"))
501+
502+
# Set as current tab if requested
503+
if set_current:
504+
self.setCurrentWidget(self.creation_scroll)
505+
498506
return True
499507

500508
def apply_creation_parameters(self) -> None:
@@ -551,19 +559,27 @@ def apply_creation_parameters(self) -> None:
551559

552560
# Refresh the Creation tab with the new parameters
553561
# Use QTimer to defer this until after the current event is processed
562+
# Set the Creation tab as current to keep it visible after refresh
554563
QC.QTimer.singleShot(
555-
0, lambda: self.setup_creation_tab(self.current_creation_obj)
564+
0,
565+
lambda: self.setup_creation_tab(
566+
self.current_creation_obj, set_current=True
567+
),
556568
)
557569

558570
def setup_processing_tab(
559-
self, obj: SignalObj | ImageObj, reset_params: bool = True
571+
self,
572+
obj: SignalObj | ImageObj,
573+
reset_params: bool = True,
574+
set_current: bool = False,
560575
) -> bool:
561576
"""Setup the Processing tab with parameter editor for re-processing.
562577
563578
Args:
564579
obj: Signal or Image object
565580
reset_params: If True, call update_from_obj() to reset parameters from
566581
source object. If False, use parameters as stored in metadata.
582+
set_current: If True, set the Processing tab as current after creation
567583
568584
Returns:
569585
True if Processing tab was set up, False otherwise
@@ -643,6 +659,11 @@ def setup_processing_tab(
643659

644660
self.processing_scroll.setWidget(editor)
645661
self.insertTab(insert_index, self.processing_scroll, _("Processing"))
662+
663+
# Set as current tab if requested
664+
if set_current:
665+
self.setCurrentWidget(self.processing_scroll)
666+
646667
return True
647668

648669
def apply_processing_parameters(
@@ -755,8 +776,12 @@ def apply_processing_parameters(
755776

756777
# Refresh the Processing tab with the new parameters
757778
# Don't reset parameters from source object - keep the user's values
779+
# Set the Processing tab as current to keep it visible after refresh
758780
QC.QTimer.singleShot(
759-
0, lambda: self.setup_processing_tab(obj, reset_params=False)
781+
0,
782+
lambda: self.setup_processing_tab(
783+
obj, reset_params=False, set_current=True
784+
),
760785
)
761786

762787
if isinstance(obj, SignalObj):

datalab/tests/features/common/interactive_processing_test.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ def test_no_duplicate_creation_tabs():
262262
263263
This test verifies the fix for the bug where clicking "Apply" in the
264264
Creation tab would create a new Creation tab instead of reusing the
265-
existing one.
265+
existing one. It also verifies that the Creation tab remains current
266+
after applying changes.
266267
"""
267268
with qt_app_context():
268269
with datalab_test_app_context() as win:
@@ -316,6 +317,12 @@ def test_no_duplicate_creation_tabs():
316317
f"applying amplitude={amplitude}"
317318
)
318319

320+
# Verify that the Creation tab is the current tab
321+
assert objprop.currentWidget() is objprop.creation_scroll, (
322+
f"Creation tab should remain current after "
323+
f"applying amplitude={amplitude}"
324+
)
325+
319326

320327
def test_no_creation_parameters_for_base_classes():
321328
"""Test that creation parameters are NOT stored for base classes
@@ -480,6 +487,87 @@ def test_apply_processing_parameters_image():
480487
assert stored_param.value == v1
481488

482489

490+
def test_no_duplicate_processing_tabs():
491+
"""Test that applying processing parameters multiple times doesn't create
492+
duplicate tabs.
493+
494+
This test verifies the fix for the bug where clicking "Apply" in the
495+
Processing tab would create a new Processing tab instead of reusing the
496+
existing one. It also verifies that the Processing tab remains current
497+
after applying changes.
498+
"""
499+
with qt_app_context():
500+
with datalab_test_app_context() as win:
501+
panel = win.imagepanel
502+
processor = panel.processor
503+
objprop = panel.objprop
504+
505+
# Create a default test image
506+
panel.new_object(edit=False)
507+
image = panel.objview.get_current_object()
508+
assert image is not None
509+
510+
# Apply addition_constant with initial value
511+
v0 = 7.0
512+
processor.run_feature("addition_constant", ConstantParam.create(value=v0))
513+
514+
# Get the processed image
515+
processed_ima = panel.objview.get_current_object()
516+
assert processed_ima is not None
517+
518+
# Select the processed image to trigger setup_processing_tab
519+
panel.objview.set_current_object(processed_ima)
520+
521+
# Verify Processing tab was set up
522+
assert objprop.processing_param_editor is not None
523+
assert objprop.processing_scroll is not None
524+
525+
# Count how many Processing tabs exist initially
526+
initial_index = objprop.indexOf(objprop.processing_scroll)
527+
assert initial_index >= 0, "Processing tab should be present"
528+
529+
# Count tabs by checking if they reference the same scroll widget
530+
initial_count = sum(
531+
1
532+
for i in range(objprop.count())
533+
if objprop.widget(i) is objprop.processing_scroll
534+
)
535+
assert initial_count == 1, (
536+
"Should have exactly one Processing tab initially"
537+
)
538+
539+
# Apply processing parameters multiple times
540+
editor = objprop.processing_param_editor
541+
for value in [10.0, 15.0, 20.0]:
542+
editor.dataset.value = value
543+
report = objprop.apply_processing_parameters()
544+
assert report.success
545+
546+
# Wait for the deferred setup_processing_tab to complete
547+
from qtpy.QtTest import QTest
548+
549+
QTest.qWait(100)
550+
551+
# Verify that processing_scroll reference still exists
552+
assert objprop.processing_scroll is not None
553+
554+
# Count Processing tabs again - should still be just one
555+
processing_count = sum(
556+
1
557+
for i in range(objprop.count())
558+
if objprop.widget(i) is objprop.processing_scroll
559+
)
560+
assert processing_count == 1, (
561+
f"Should still have exactly one Processing tab after "
562+
f"applying value={value}"
563+
)
564+
565+
# Verify that the Processing tab is the current tab
566+
assert objprop.currentWidget() is objprop.processing_scroll, (
567+
f"Processing tab should remain current after applying value={value}"
568+
)
569+
570+
483571
def test_apply_processing_parameters_missing_source():
484572
"""Test apply_processing_parameters when source object is missing"""
485573
with qt_app_context():
@@ -874,6 +962,7 @@ def test_select_source_objects_deleted_source():
874962
test_no_creation_parameters_for_base_classes()
875963
test_apply_processing_parameters_signal()
876964
test_apply_processing_parameters_image()
965+
test_no_duplicate_processing_tabs()
877966
test_apply_processing_parameters_missing_source()
878967
test_cross_panel_image_to_signal()
879968
test_cross_panel_image_to_signal_group()

0 commit comments

Comments
 (0)