From 7c44a968a8088791dd874f4fe5ba6266067d5a97 Mon Sep 17 00:00:00 2001 From: Qinyao Weng Date: Thu, 27 Aug 2026 14:52:04 +0100 Subject: [PATCH 1/3] fixing the test_mainwindow and added merge_focus testing --- .../tests/test_mainwindow_merge_focus.py | 186 ++++++++++++++++++ spikeinterface_gui/tests/testingtools.py | 2 +- 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 spikeinterface_gui/tests/test_mainwindow_merge_focus.py diff --git a/spikeinterface_gui/tests/test_mainwindow_merge_focus.py b/spikeinterface_gui/tests/test_mainwindow_merge_focus.py new file mode 100644 index 0000000..b749954 --- /dev/null +++ b/spikeinterface_gui/tests/test_mainwindow_merge_focus.py @@ -0,0 +1,186 @@ +from argparse import ArgumentParser +from spikeinterface_gui import run_mainwindow, run_launcher + +from spikeinterface_gui.tests.testingtools import clean_all, make_analyzer_folder, make_curation_dict + +from spikeinterface import load_sorting_analyzer + + +from pathlib import Path + +import numpy as np +import sys + + +# yep is for testing +yep_layout = dict( + zone1=['curation', 'spikelist'], + zone2=['unitlist', 'mergelist'], + zone3=['trace', 'tracemap', 'spikeamplitude'], + zone4=['similarity'], + zone5=['probe'], + zone6=['ndscatter', ], + zone7=['waveform', 'waveformheatmap', ], + zone8=['correlogram', 'isi'], +) + + +def setup_module(): + global test_folder + case = test_folder.stem.split('_')[-1] + make_analyzer_folder(test_folder, case=case, unit_dtype="int") + + +def teardown_module(): + clean_all(test_folder) + + +def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False): + + analyzer = load_sorting_analyzer(test_folder / "sorting_analyzer") + # analyzer = load_analyzer(test_folder / "sorting_analyzer.zarr") + + tm = analyzer.get_extension("template_metrics").get_data().iloc[0, :] + # print(tm) + # return + + print(analyzer) + + if curation: + curation_dict = make_curation_dict(analyzer) + else: + curation_dict = None + + if only_some_extensions: + analyzer = analyzer.copy() + # analyzer._recording = None + for k in ("principal_components", "template_similarity", "spike_amplitudes"): + analyzer.delete_extension(k) + print(analyzer) + + n = analyzer.unit_ids.size + analyzer.sorting.set_property( + key='yep', values=np.array([f"yep{i}" for i in range(n)])) + + extra_unit_properties = dict( + yop=np.array([f"yop{i}" for i in range(n)]), + yip=np.array([f"yip{i}" for i in range(n)]), + ) + + for segment_index in range(analyzer.get_num_segments()): + shift = (segment_index + 1) * 100 + # add a gap to times + gap = 5 + times = analyzer.recording.get_times(segment_index) + times = times + shift + times[len(times)//2:] += gap # add a gap in the middle + analyzer.recording.set_times( + times, + segment_index=segment_index + ) + + events_dict = None + if events: + events_dict = {"event1": {"times": []}, "event2": {"times": []}} + for segment_index in range(analyzer.get_num_segments()): + times = analyzer.recording.get_times(segment_index) + events_dict["event1"]["times"].append( + np.random.choice(times, 30) + ) + events_dict["event2"]["times"].append( + np.random.choice(times, 50) + ) + # add some events outside of recording times to test filtering + events_dict["event1"]["times"][-1] = np.concatenate( + [events_dict["event1"]["times"][-1], + [times[0] - 10, times[-1] + 20]] + ) + events_dict["event2"]["times"][-1] = np.concatenate( + [events_dict["event2"]["times"][-1], + [times[0] - 5, times[-1] + 15]] + ) + + win = run_mainwindow( + analyzer, + mode="desktop", + start_app=start_app, + verbose=verbose, + curation=curation, curation_dict=curation_dict, + displayed_unit_properties=None, + extra_unit_properties=extra_unit_properties, + layout_preset='default', + events=events_dict + # user_settings={"mainsettings": {"color_mode": "color_by_visibility", "max_visible_units": 5}} + ) + + +def test_launcher(verbose=True): + + # case 1 + analyzer_folders = None + root_folder = None + + # case 2 : explore parent + analyzer_folders = None + root_folder = Path(__file__).parent + + # case 3 : list + # analyzer_folders = [ + # Path(__file__).parent / 'my_dataset_small/sorting_analyzer', + # Path(__file__).parent / 'my_dataset_big/sorting_analyzer', + # ] + # root_folder = None + + # case 4 : dict + # analyzer_folders = { + # 'small' : Path(__file__).parent / 'my_dataset_small/sorting_analyzer', + # 'big' : Path(__file__).parent / 'my_dataset_big/sorting_analyzer', + # } + # root_folder = None + + win = run_launcher(mode="desktop", analyzer_folders=analyzer_folders, + root_folder=root_folder, verbose=verbose) + + +def test_main_window_merge_focus(start_app=False, Verbose=True): + analyzer = load_sorting_analyzer(test_folder/"sorting_analyzer") + if not analyzer.has_extension("template_similarity"): + analyzer.compute_one_extension("template_similarity") + merge_unit_groups = sc.compute_merge_unit_groups( + analyzer, + preset="slay", + ) + print("Computed merge unit groups:", merge_unit_groups) + win = run_mainwindow( + analyzer, + mode="desktop", + start_app=start_app, + verbose=verbose, + curation=True, + layout_preset="merge_focus", + merge_unit_groups=merge_unit_groups, + ) + return win + + +parser = ArgumentParser() +parser.add_argument('--dataset', default="small", + help='Path to the dataset folder') +parser.add_argument('--events', action="store_true", + help='Simulate and add events') + +if __name__ == '__main__': + args = parser.parse_args() + dataset = args.dataset + global test_folder + if dataset is not None: + test_folder = Path(__file__).parents[2] / f"my_dataset_{dataset}" + + if not test_folder.is_dir(): + setup_module() + + win = test_mainwindow(start_app=True, verbose=True, + curation=True, events=args.events) + # win = test_mainwindow(start_app=True, verbose=True, curation=False) + + # test_launcher(verbose=True) diff --git a/spikeinterface_gui/tests/testingtools.py b/spikeinterface_gui/tests/testingtools.py index ed0f0a5..e44b4a0 100644 --- a/spikeinterface_gui/tests/testingtools.py +++ b/spikeinterface_gui/tests/testingtools.py @@ -138,9 +138,9 @@ def make_analyzer_folder(test_folder, case="small", unit_dtype="str"): sorting_analyzer.compute("correlograms", window_ms=50., bin_ms=1.) sorting_analyzer.compute("template_similarity", method="l1") sorting_analyzer.compute("principal_components", n_components=3, mode='by_channel_global', whiten=True, **job_kwargs) + sorting_analyzer.compute(["spike_amplitudes", "spike_locations"], **job_kwargs) sorting_analyzer.compute("quality_metrics", metric_names=["snr", "firing_rate"]) sorting_analyzer.compute("template_metrics") - sorting_analyzer.compute(["spike_amplitudes", "spike_locations"], **job_kwargs) def make_curation_dict(analyzer): From 3fbbe05cac5eb73e7c54f60ca7dd3e66cc1b492a Mon Sep 17 00:00:00 2001 From: Qinyao Weng Date: Thu, 27 Aug 2026 15:14:52 +0100 Subject: [PATCH 2/3] change the preset view by adding the curation panel when running merge focu --- spikeinterface_gui/layout_presets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spikeinterface_gui/layout_presets.py b/spikeinterface_gui/layout_presets.py index 73d3097..8619257 100644 --- a/spikeinterface_gui/layout_presets.py +++ b/spikeinterface_gui/layout_presets.py @@ -93,12 +93,12 @@ def get_layout_description(preset_name, layout=None): merge_focus_layout = dict( zone1=['merge', 'unitlist'], - zone2=['waveform'], + zone2=['curation'], zone3=['spikeamplitude'], zone4=['ndscatter'], zone5=['probe'], zone6=[], - zone7=['spikerate'], + zone7=['waveform'], zone8=['correlogram'], ) From e24aaf89b1b4c431385c25e7fc80930959f6193f Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Mon, 7 Sep 2026 10:06:29 +0200 Subject: [PATCH 3/3] feat: add layout cli arg to test scripts --- .../tests/test_mainwindow_merge_focus.py | 186 ------------------ .../tests/test_mainwindow_panel.py | 7 +- .../tests/test_mainwindow_qt.py | 7 +- 3 files changed, 8 insertions(+), 192 deletions(-) delete mode 100644 spikeinterface_gui/tests/test_mainwindow_merge_focus.py diff --git a/spikeinterface_gui/tests/test_mainwindow_merge_focus.py b/spikeinterface_gui/tests/test_mainwindow_merge_focus.py deleted file mode 100644 index b749954..0000000 --- a/spikeinterface_gui/tests/test_mainwindow_merge_focus.py +++ /dev/null @@ -1,186 +0,0 @@ -from argparse import ArgumentParser -from spikeinterface_gui import run_mainwindow, run_launcher - -from spikeinterface_gui.tests.testingtools import clean_all, make_analyzer_folder, make_curation_dict - -from spikeinterface import load_sorting_analyzer - - -from pathlib import Path - -import numpy as np -import sys - - -# yep is for testing -yep_layout = dict( - zone1=['curation', 'spikelist'], - zone2=['unitlist', 'mergelist'], - zone3=['trace', 'tracemap', 'spikeamplitude'], - zone4=['similarity'], - zone5=['probe'], - zone6=['ndscatter', ], - zone7=['waveform', 'waveformheatmap', ], - zone8=['correlogram', 'isi'], -) - - -def setup_module(): - global test_folder - case = test_folder.stem.split('_')[-1] - make_analyzer_folder(test_folder, case=case, unit_dtype="int") - - -def teardown_module(): - clean_all(test_folder) - - -def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False): - - analyzer = load_sorting_analyzer(test_folder / "sorting_analyzer") - # analyzer = load_analyzer(test_folder / "sorting_analyzer.zarr") - - tm = analyzer.get_extension("template_metrics").get_data().iloc[0, :] - # print(tm) - # return - - print(analyzer) - - if curation: - curation_dict = make_curation_dict(analyzer) - else: - curation_dict = None - - if only_some_extensions: - analyzer = analyzer.copy() - # analyzer._recording = None - for k in ("principal_components", "template_similarity", "spike_amplitudes"): - analyzer.delete_extension(k) - print(analyzer) - - n = analyzer.unit_ids.size - analyzer.sorting.set_property( - key='yep', values=np.array([f"yep{i}" for i in range(n)])) - - extra_unit_properties = dict( - yop=np.array([f"yop{i}" for i in range(n)]), - yip=np.array([f"yip{i}" for i in range(n)]), - ) - - for segment_index in range(analyzer.get_num_segments()): - shift = (segment_index + 1) * 100 - # add a gap to times - gap = 5 - times = analyzer.recording.get_times(segment_index) - times = times + shift - times[len(times)//2:] += gap # add a gap in the middle - analyzer.recording.set_times( - times, - segment_index=segment_index - ) - - events_dict = None - if events: - events_dict = {"event1": {"times": []}, "event2": {"times": []}} - for segment_index in range(analyzer.get_num_segments()): - times = analyzer.recording.get_times(segment_index) - events_dict["event1"]["times"].append( - np.random.choice(times, 30) - ) - events_dict["event2"]["times"].append( - np.random.choice(times, 50) - ) - # add some events outside of recording times to test filtering - events_dict["event1"]["times"][-1] = np.concatenate( - [events_dict["event1"]["times"][-1], - [times[0] - 10, times[-1] + 20]] - ) - events_dict["event2"]["times"][-1] = np.concatenate( - [events_dict["event2"]["times"][-1], - [times[0] - 5, times[-1] + 15]] - ) - - win = run_mainwindow( - analyzer, - mode="desktop", - start_app=start_app, - verbose=verbose, - curation=curation, curation_dict=curation_dict, - displayed_unit_properties=None, - extra_unit_properties=extra_unit_properties, - layout_preset='default', - events=events_dict - # user_settings={"mainsettings": {"color_mode": "color_by_visibility", "max_visible_units": 5}} - ) - - -def test_launcher(verbose=True): - - # case 1 - analyzer_folders = None - root_folder = None - - # case 2 : explore parent - analyzer_folders = None - root_folder = Path(__file__).parent - - # case 3 : list - # analyzer_folders = [ - # Path(__file__).parent / 'my_dataset_small/sorting_analyzer', - # Path(__file__).parent / 'my_dataset_big/sorting_analyzer', - # ] - # root_folder = None - - # case 4 : dict - # analyzer_folders = { - # 'small' : Path(__file__).parent / 'my_dataset_small/sorting_analyzer', - # 'big' : Path(__file__).parent / 'my_dataset_big/sorting_analyzer', - # } - # root_folder = None - - win = run_launcher(mode="desktop", analyzer_folders=analyzer_folders, - root_folder=root_folder, verbose=verbose) - - -def test_main_window_merge_focus(start_app=False, Verbose=True): - analyzer = load_sorting_analyzer(test_folder/"sorting_analyzer") - if not analyzer.has_extension("template_similarity"): - analyzer.compute_one_extension("template_similarity") - merge_unit_groups = sc.compute_merge_unit_groups( - analyzer, - preset="slay", - ) - print("Computed merge unit groups:", merge_unit_groups) - win = run_mainwindow( - analyzer, - mode="desktop", - start_app=start_app, - verbose=verbose, - curation=True, - layout_preset="merge_focus", - merge_unit_groups=merge_unit_groups, - ) - return win - - -parser = ArgumentParser() -parser.add_argument('--dataset', default="small", - help='Path to the dataset folder') -parser.add_argument('--events', action="store_true", - help='Simulate and add events') - -if __name__ == '__main__': - args = parser.parse_args() - dataset = args.dataset - global test_folder - if dataset is not None: - test_folder = Path(__file__).parents[2] / f"my_dataset_{dataset}" - - if not test_folder.is_dir(): - setup_module() - - win = test_mainwindow(start_app=True, verbose=True, - curation=True, events=args.events) - # win = test_mainwindow(start_app=True, verbose=True, curation=False) - - # test_launcher(verbose=True) diff --git a/spikeinterface_gui/tests/test_mainwindow_panel.py b/spikeinterface_gui/tests/test_mainwindow_panel.py index 05d5e19..219e1b8 100644 --- a/spikeinterface_gui/tests/test_mainwindow_panel.py +++ b/spikeinterface_gui/tests/test_mainwindow_panel.py @@ -20,7 +20,7 @@ def teardown_module(): clean_all(test_folder) -def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False, port=0): +def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False, port=0, layout="default"): analyzer = load_sorting_analyzer(test_folder / "sorting_analyzer") @@ -78,7 +78,7 @@ def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_ext curation=curation, curation_dict=curation_dict, displayed_unit_properties=None, extra_unit_properties=extra_unit_properties, - layout_preset='default', + layout_preset=layout, # address="10.69.168.40", port=port, events=events_dict @@ -112,6 +112,7 @@ def test_launcher(verbose=True): parser = ArgumentParser() parser.add_argument('--dataset', default="small", help='Path to the dataset folder') parser.add_argument('--events', action="store_true", help='Simulate and add events') +parser.add_argument('--layout', default="default", help='Layout of the GUI, default is "default"') if __name__ == '__main__': args = parser.parse_args() @@ -123,7 +124,7 @@ def test_launcher(verbose=True): if not test_folder.is_dir(): setup_module() - win = test_mainwindow(start_app=True, verbose=True, curation=True, events=args.events, port=0) + win = test_mainwindow(start_app=True, verbose=True, curation=True, events=args.events, port=0, layout=args.layout) # test_launcher(verbose=True) diff --git a/spikeinterface_gui/tests/test_mainwindow_qt.py b/spikeinterface_gui/tests/test_mainwindow_qt.py index 0fbc3ef..8aeaa30 100644 --- a/spikeinterface_gui/tests/test_mainwindow_qt.py +++ b/spikeinterface_gui/tests/test_mainwindow_qt.py @@ -35,7 +35,7 @@ def teardown_module(): clean_all(test_folder) -def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False): +def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_extensions=False, events=False, layout="default"): analyzer = load_sorting_analyzer(test_folder / "sorting_analyzer") @@ -108,7 +108,7 @@ def test_mainwindow(start_app=False, verbose=True, curation=False, only_some_ext curation=curation, curation_dict=curation_dict, displayed_unit_properties=None, extra_unit_properties=extra_unit_properties, - layout_preset='default', + layout_preset=layout, events=events_dict # user_settings={"mainsettings": {"color_mode": "color_by_visibility", "max_visible_units": 5}} ) @@ -144,6 +144,7 @@ def test_launcher(verbose=True): parser = ArgumentParser() parser.add_argument('--dataset', default="small", help='Path to the dataset folder') parser.add_argument('--events', action="store_true", help='Simulate and add events') +parser.add_argument('--layout', default="default", help='Enable layout preset') if __name__ == '__main__': args = parser.parse_args() @@ -155,7 +156,7 @@ def test_launcher(verbose=True): if not test_folder.is_dir(): setup_module() - win = test_mainwindow(start_app=True, verbose=True, curation=True, events=args.events) + win = test_mainwindow(start_app=True, verbose=True, curation=True, events=args.events, layout=args.layout) # win = test_mainwindow(start_app=True, verbose=True, curation=False) # test_launcher(verbose=True)