Skip to content

Commit 9ebf871

Browse files
Remove _add_images_to_existing_mixed_dataset and _add_recon_to_dataset_and_tree_view
1 parent eaf970c commit 9ebf871

File tree

3 files changed

+74
-136
lines changed

3 files changed

+74
-136
lines changed

mantidimaging/core/data/dataset.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ def set_stack(self, file_type: FILE_TYPES, image_stack: ImageStack) -> None:
178178
else:
179179
raise AttributeError(f"StrictDataset does not have an attribute for {attr_name}")
180180

181+
def set_stack_by_type_name(self, file_type_name: str, image_stack: ImageStack) -> None:
182+
file_type_name = file_type_name.upper().replace(" ", "_")
183+
if file_type_name == "RECON":
184+
self.add_recon(image_stack)
185+
elif file_type_name == "IMAGES":
186+
self.stacks.append(image_stack)
187+
else:
188+
file_type = getattr(FILE_TYPES, file_type_name)
189+
self.set_stack(file_type, image_stack)
190+
181191
@property
182192
def is_processed(self) -> bool:
183193
"""

mantidimaging/gui/windows/main/presenter.py

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from qt_material import apply_stylesheet
1717

1818
from mantidimaging.core.data import ImageStack
19-
from mantidimaging.core.data.dataset import MixedDataset, _get_stack_data_type, Dataset
19+
from mantidimaging.core.data.dataset import _get_stack_data_type, Dataset
2020
from mantidimaging.core.io.loader.loader import create_loading_parameters_for_file_path
2121
from mantidimaging.core.io.utility import find_projection_closest_to_180, THRESHOLD_180
2222
from mantidimaging.core.utility.data_containers import ProjectionAngles
@@ -428,7 +428,7 @@ def add_180_deg_file_to_dataset(self, dataset_id: uuid.UUID, _180_deg_file: str)
428428
self.add_child_item_to_tree_view(dataset_id, _180_deg.id, "180")
429429
else:
430430
self.replace_child_item_id(dataset_id, existing_180_id, _180_deg.id)
431-
self._delete_stack(existing_180_id)
431+
self._delete_stack_visualiser(existing_180_id)
432432

433433
self.view.model_changed.emit()
434434

@@ -576,7 +576,7 @@ def _delete_container(self, container_id: uuid.UUID) -> None:
576576
removed_stack_ids = self.model.remove_container(container_id)
577577
for stack_id in removed_stack_ids:
578578
if stack_id in self.stack_visualisers:
579-
self._delete_stack(stack_id)
579+
self._delete_stack_visualiser(stack_id)
580580

581581
# If the container_id provided is not a stack id then we remove the entire container from the tree view,
582582
# otherwise we remove the individual stacks that were deleted
@@ -586,7 +586,7 @@ def _delete_container(self, container_id: uuid.UUID) -> None:
586586

587587
self.view.model_changed.emit()
588588

589-
def _delete_stack(self, stack_id: uuid.UUID) -> None:
589+
def _delete_stack_visualiser(self, stack_id: uuid.UUID) -> None:
590590
"""
591591
Deletes a stack and frees memory.
592592
:param stack_id: The ID of the stack to delete.
@@ -631,7 +631,7 @@ def add_sinograms_to_dataset_and_update_view(self, sino_stack: ImageStack, origi
631631
parent_id = self.model.get_parent_dataset(original_stack_id)
632632
prev_sino = self.model.datasets[parent_id].sinograms
633633
if prev_sino is not None:
634-
self._delete_stack(prev_sino.id)
634+
self._delete_stack_visualiser(prev_sino.id)
635635
self.model.datasets[parent_id].sinograms = sino_stack
636636
self._add_sinograms_to_tree_view(sino_stack.id, parent_id)
637637
self.create_single_tabbed_images_stack(sino_stack)
@@ -684,36 +684,15 @@ def _add_images_to_existing_dataset(self) -> None:
684684
assert dataset is not None
685685

686686
new_images = self.view.add_to_dataset_dialog.presenter.images
687+
images_type = self.view.add_to_dataset_dialog.images_type
687688

688-
if self.view.add_to_dataset_dialog.images_type == RECON_TEXT:
689-
self._add_recon_to_dataset_and_tree_view(dataset, new_images)
690-
elif isinstance(dataset, MixedDataset):
691-
self._add_images_to_existing_mixed_dataset(dataset, new_images)
692-
else:
693-
self._add_images_to_existing_strict_dataset(dataset, new_images,
694-
self.view.add_to_dataset_dialog.images_type)
689+
dataset.set_stack_by_type_name(images_type, new_images)
695690

696691
self.create_single_tabbed_images_stack(new_images)
692+
self.update_dataset_tree()
693+
self._close_unused_visualisers()
697694
self.view.model_changed.emit()
698695

699-
def _add_recon_to_dataset_and_tree_view(self, dataset: Dataset, recon: ImageStack) -> None:
700-
"""
701-
Adds a recon to the dataset and updates the tree view.
702-
:param dataset: The dataset.
703-
:param recon: The recon ImageStack.
704-
"""
705-
dataset.add_recon(recon)
706-
self.add_recon_item_to_tree_view(dataset.id, recon.id, recon.name)
707-
708-
def _add_images_to_existing_mixed_dataset(self, dataset: Dataset, new_images: ImageStack) -> None:
709-
"""
710-
Updates the stack list in the mixed dataset and updates the tree view.
711-
:param dataset: The MixedDataset to update.
712-
:param new_images: The new images to add.
713-
"""
714-
dataset.add_stack(new_images)
715-
self.add_child_item_to_tree_view(dataset.id, new_images.id, new_images.name)
716-
717696
def _add_images_to_existing_strict_dataset(self, dataset: Dataset, new_images: ImageStack, stack_type: str) -> None:
718697
"""
719698
Adds or replaces images in a StrictDataset and updates the tree view if required.
@@ -722,21 +701,15 @@ def _add_images_to_existing_strict_dataset(self, dataset: Dataset, new_images: I
722701
"""
723702
image_attr = stack_type.replace(" ", "_").lower()
724703
new_images.name = self._create_strict_dataset_stack_name(stack_type, dataset.name)
725-
726-
if getattr(dataset, image_attr) is None:
727-
# the image type doesn't exist in the dataset
728-
self.add_child_item_to_tree_view(dataset.id, new_images.id, stack_type)
729-
730-
else:
731-
# the image type already exists in the dataset and needs to be replaced
732-
prev_images_id = getattr(dataset, image_attr).id
733-
if image_attr == "sample" and dataset.proj180deg:
734-
self._delete_stack(dataset.proj180deg.id)
735-
self.remove_item_from_tree_view(dataset.proj180deg.id)
736-
self.replace_child_item_id(dataset.id, prev_images_id, new_images.id)
737-
self._delete_stack(prev_images_id)
738-
739704
setattr(dataset, image_attr, new_images)
705+
self.update_dataset_tree()
706+
707+
def _close_unused_visualisers(self):
708+
visualisers = set(self.stack_visualisers.keys())
709+
stacks = {stack.id for stack in self.get_all_stacks()}
710+
removed = visualisers - stacks
711+
for stack_id in removed:
712+
self._delete_stack_visualiser(stack_id)
740713

741714
def _move_stack(self, origin_dataset_id: uuid.UUID, stack_id: uuid.UUID, destination_stack_type: str,
742715
destination_dataset_id: uuid.UUID) -> None:
@@ -757,20 +730,9 @@ def _move_stack(self, origin_dataset_id: uuid.UUID, stack_id: uuid.UUID, destina
757730
f"Unable to find destination dataset with ID {destination_dataset_id} when attempting to move stack")
758731

759732
stack_to_move = self.get_stack(stack_id)
760-
self.remove_item_from_tree_view(stack_id)
761-
762-
if destination_stack_type is RECON_TEXT:
763-
self._add_recon_to_dataset_and_tree_view(destination_dataset, stack_to_move)
764-
elif isinstance(destination_dataset, MixedDataset):
765-
self._add_images_to_existing_mixed_dataset(destination_dataset, stack_to_move)
766-
else:
767-
assert self.view.move_stack_dialog is not None
768-
data_type = self.view.move_stack_dialog.destination_stack_type
769-
self._add_images_to_existing_strict_dataset(destination_dataset, stack_to_move, data_type)
770-
stack_to_move.name = self._create_strict_dataset_stack_name(data_type, destination_dataset.name)
771-
772733
origin_dataset.delete_stack(stack_id)
773-
self.view.model_changed.emit()
734+
self._add_images_to_existing_strict_dataset(destination_dataset, stack_to_move, destination_stack_type)
735+
self._close_unused_visualisers()
774736

775737
@staticmethod
776738
def _create_strict_dataset_stack_name(stack_type: str, dataset_name: str) -> str:

mantidimaging/gui/windows/main/test/presenter_test.py

Lines changed: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from unittest.mock import patch, call
1010

1111
import numpy as np
12+
from parameterized import parameterized
1213

1314
from mantidimaging.core.data import ImageStack
1415
from mantidimaging.core.data.dataset import StrictDataset, MixedDataset, Dataset
@@ -621,11 +622,11 @@ def test_add_sinograms_to_dataset_with_no_sinograms_and_update_view(self):
621622
dataset_item_mock.id = ds.id
622623
self.view.get_sinograms_item.return_value = None
623624
self.presenter.create_single_tabbed_images_stack = mock.Mock()
624-
self.presenter._delete_stack = mock.Mock()
625+
self.presenter._delete_stack_visualiser = mock.Mock()
625626

626627
self.presenter.add_sinograms_to_dataset_and_update_view(sinograms, ds.sample.id)
627628
self.model.get_parent_dataset.assert_called_once_with(ds.sample.id)
628-
self.presenter._delete_stack.assert_not_called()
629+
self.presenter._delete_stack_visualiser.assert_not_called()
629630
self.assertIs(ds.sinograms, sinograms)
630631
self.view.get_dataset_tree_view_item.assert_called_once_with(ds.id)
631632
self.view.get_sinograms_item.assert_called_once_with(dataset_item_mock)
@@ -644,10 +645,10 @@ def test_add_sinograms_to_dataset_with_existing_sinograms_and_update_view(self):
644645
dataset_item_mock.id = ds.id
645646
sinograms_item_mock = self.view.get_sinograms_item.return_value
646647
self.presenter.create_single_tabbed_images_stack = mock.Mock()
647-
self.presenter._delete_stack = mock.Mock()
648+
self.presenter._delete_stack_visualiser = mock.Mock()
648649

649650
self.presenter.add_sinograms_to_dataset_and_update_view(new_sinograms, ds.sample.id)
650-
self.presenter._delete_stack.assert_called_once_with(existing_sinograms.id)
651+
self.presenter._delete_stack_visualiser.assert_called_once_with(existing_sinograms.id)
651652
self.assertIs(ds.sinograms, new_sinograms)
652653
self.view.get_dataset_tree_view_item.assert_called_once_with(ds.id)
653654
self.view.get_sinograms_item.assert_called_once_with(dataset_item_mock)
@@ -764,87 +765,36 @@ def test_show_add_stack_to_dataset_dialog_called_with_stack_id(self):
764765
self.presenter.notify(Notification.SHOW_ADD_STACK_DIALOG, container_id="stack-id")
765766
self.view.show_add_stack_to_existing_dataset_dialog.assert_called_once_with(dataset_id)
766767

767-
def test_add_new_stack_to_strict_dataset(self):
768+
@parameterized.expand(["Sample", "Flat Before", "Flat After", "Dark Before", "Dark After", "Recon", "Images"])
769+
def test_add_new_stack_to_dataset(self, images_type):
768770
self.dataset.flat_before = None
769771
self.model.datasets[self.dataset.id] = self.dataset
770772

771773
self.view.add_to_dataset_dialog = mock.Mock()
772774
self.view.add_to_dataset_dialog.dataset_id = self.dataset.id
773775
self.view.add_to_dataset_dialog.presenter.images = new_images = generate_images()
774-
self.view.add_to_dataset_dialog.images_type = images_type = "Flat Before"
776+
self.view.add_to_dataset_dialog.images_type = images_type
775777

776-
self.presenter.add_child_item_to_tree_view = mock.Mock()
777778
self.presenter.create_single_tabbed_images_stack = mock.Mock()
779+
self.presenter._close_unused_visualisers = mock.Mock()
780+
self.view.add_toplevel_item_to_dataset_tree_widget.return_value = mock_top_item = mock.Mock()
781+
self.view.add_item_to_dataset_tree_widget.return_value = mock_top_item
778782

779-
self.presenter._add_images_to_existing_dataset()
780-
self.presenter.add_child_item_to_tree_view.assert_called_once_with(self.dataset.id, new_images.id, images_type)
781-
self.presenter.create_single_tabbed_images_stack.assert_called_once_with(new_images)
782-
self.view.model_changed.emit.assert_called_once()
783-
self.assertIs(self.dataset.flat_before, new_images)
784-
785-
def test_replace_existing_stack_in_strict_dataset(self):
786-
self.model.datasets[self.dataset.id] = self.dataset
787-
prev_images_id = self.dataset.flat_before.id
783+
treeview_label = images_type
784+
if images_type == "Sample":
785+
treeview_label = "Projections"
786+
elif images_type in ["Recon", "Images"]:
787+
treeview_label = new_images.name
788788

789-
self.view.add_to_dataset_dialog = mock.Mock()
790-
self.view.add_to_dataset_dialog.dataset_id = self.dataset.id
791-
self.view.add_to_dataset_dialog.presenter.images = new_images = generate_images()
792-
self.view.add_to_dataset_dialog.images_type = "Flat Before"
789+
self.presenter._add_images_to_existing_dataset()
793790

794-
self.presenter.create_single_tabbed_images_stack = mock.Mock()
795-
self.presenter.replace_child_item_id = mock.Mock()
796-
self.presenter._delete_stack = mock.Mock()
791+
self.assertIn(call(treeview_label, new_images.id, mock_top_item),
792+
self.view.add_item_to_dataset_tree_widget.mock_calls)
797793

798-
self.presenter.notify(Notification.DATASET_ADD)
799-
self.presenter.replace_child_item_id.assert_called_once_with(self.dataset.id, prev_images_id, new_images.id)
800-
self.presenter._delete_stack.assert_called_once_with(prev_images_id)
801794
self.presenter.create_single_tabbed_images_stack.assert_called_once_with(new_images)
802795
self.view.model_changed.emit.assert_called_once()
803-
self.assertIs(self.dataset.flat_before, new_images)
804-
805-
def test_add_stack_to_mixed_dataset(self):
806-
mixed_dataset = MixedDataset()
807-
self.model.datasets[mixed_dataset.id] = mixed_dataset
808-
809-
self.view.add_to_dataset_dialog = mock.Mock()
810-
self.view.add_to_dataset_dialog.dataset_id = mixed_dataset.id
811-
self.view.add_to_dataset_dialog.presenter.images = new_images = generate_images()
812-
self.presenter.add_child_item_to_tree_view = mock.Mock()
813-
814-
self.presenter._add_images_to_existing_dataset()
815-
self.assertIn(new_images, mixed_dataset.all)
816-
self.presenter.add_child_item_to_tree_view.assert_called_once_with(mixed_dataset.id, new_images.id,
817-
new_images.name)
818-
819-
def test_add_recon_to_mixed_dataset(self):
820-
mixed_dataset = MixedDataset(stacks=[generate_images()])
821-
recon = generate_images()
822-
recon.name = "recon-name"
823-
824-
self.view.add_to_dataset_dialog.images_type = RECON_TEXT
825-
self.presenter.add_recon_item_to_tree_view = mock.Mock()
826-
self.presenter.get_dataset = mock.Mock(return_value=mixed_dataset)
827-
self.view.add_to_dataset_dialog.presenter.images = recon
828-
829-
self.presenter._add_images_to_existing_dataset()
830-
831-
self.assertIn(recon, mixed_dataset.recons.stacks)
832-
self.presenter.add_recon_item_to_tree_view.assert_called_once_with(mixed_dataset.id, recon.id, recon.name)
833-
834-
def test_add_recon_to_strict_dataset(self):
835-
self.presenter.add_recon_item_to_tree_view = mock.Mock()
836-
self.view.add_to_dataset_dialog.images_type = RECON_TEXT
837-
838-
recon = generate_images()
839-
recon.name = "recon-name"
840-
self.presenter.add_recon_item_to_tree_view = mock.Mock()
841-
self.presenter.get_dataset = mock.Mock(return_value=self.dataset)
842-
self.view.add_to_dataset_dialog.presenter.images = recon
843-
844-
self.presenter._add_images_to_existing_dataset()
845-
846-
self.assertIn(recon, self.dataset.recons.stacks)
847-
self.presenter.add_recon_item_to_tree_view.assert_called_once_with(self.dataset.id, recon.id, recon.name)
796+
self.assertIn(new_images, self.dataset.all)
797+
self.presenter._close_unused_visualisers.assert_called_once()
848798

849799
def test_select_tree_widget_item(self):
850800
tree_widget_item = mock.Mock()
@@ -949,7 +899,7 @@ def test_stack_moved_to_recon(self):
949899

950900
self.presenter.get_stack = mock.Mock(return_value=stack_to_move)
951901
self.presenter.remove_item_from_tree_view = mock.Mock()
952-
self.presenter._add_recon_to_dataset_and_tree_view = mock.Mock()
902+
self.presenter._add_images_to_existing_strict_dataset = mock.Mock()
953903
self.presenter.get_dataset = mock.Mock(side_effect=[origin_dataset, destination_dataset])
954904

955905
self.presenter.notify(Notification.MOVE_STACK,
@@ -958,7 +908,8 @@ def test_stack_moved_to_recon(self):
958908
destination_stack_type=RECON_TEXT,
959909
destination_dataset_id=destination_dataset_id)
960910
self.presenter.get_stack.assert_called_once_with(stack_to_move.id)
961-
self.presenter._add_recon_to_dataset_and_tree_view.assert_called_once_with(destination_dataset, stack_to_move)
911+
self.presenter._add_images_to_existing_strict_dataset.assert_called_once_with(
912+
destination_dataset, stack_to_move, RECON_TEXT)
962913

963914
self.assertNotIn(stack_to_move, origin_dataset)
964915

@@ -971,34 +922,34 @@ def test_stack_moved_to_mixed_dataset_images(self):
971922
self.presenter.get_dataset = mock.Mock(side_effect=[origin_dataset, destination_dataset])
972923
self.presenter.get_stack = mock.Mock(return_value=stack_to_move)
973924
self.presenter.remove_item_from_tree_view = mock.Mock()
974-
self.presenter._add_images_to_existing_mixed_dataset = mock.Mock()
925+
self.presenter._add_images_to_existing_strict_dataset = mock.Mock()
975926

976927
self.presenter._move_stack(origin_dataset.id, stack_to_move.id, "Images", destination_dataset_id)
977928
self.presenter.get_stack.assert_called_once_with(stack_to_move.id)
978-
self.presenter._add_images_to_existing_mixed_dataset.assert_called_once_with(destination_dataset, stack_to_move)
929+
self.presenter._add_images_to_existing_strict_dataset.assert_called_once_with(
930+
destination_dataset, stack_to_move, "Images")
979931

980932
self.assertNotIn(stack_to_move, origin_dataset)
981933

982934
def test_move_stack_to_strict_dataset(self):
983935
stack_to_move = generate_images()
936+
print(f"{stack_to_move.name=}")
984937
origin_dataset = MixedDataset(stacks=[stack_to_move])
985938
destination_dataset = StrictDataset(sample=generate_images())
986939
self.presenter.get_dataset = mock.Mock(side_effect=[origin_dataset, destination_dataset])
987940
self.presenter.get_stack = mock.Mock(return_value=stack_to_move)
988941

989942
self.presenter.remove_item_from_tree_view = mock.Mock()
990-
self.presenter._add_images_to_existing_strict_dataset = mock.Mock()
943+
self.presenter.update_dataset_tree = mock.Mock()
991944

992945
self.view.move_stack_dialog = mock.Mock()
993946
self.view.move_stack_dialog.destination_stack_type = data_type = "Flat After"
994-
self._add_images_to_existing_strict_dataset = mock.Mock()
995947
new_stack_name = "New Dataset Flat After"
996948
self.presenter._create_strict_dataset_stack_name = mock.Mock(return_value=new_stack_name)
997949

998950
self.presenter._move_stack(origin_dataset.id, stack_to_move.id, data_type, destination_dataset.id)
999951
self.presenter.get_stack.assert_called_once_with(stack_to_move.id)
1000-
self.presenter._add_images_to_existing_strict_dataset.assert_called_once_with(
1001-
destination_dataset, stack_to_move, data_type)
952+
self.presenter.update_dataset_tree.assert_called_once()
1002953

1003954
self.assertNotIn(stack_to_move, origin_dataset)
1004955
assert stack_to_move.name == new_stack_name
@@ -1107,6 +1058,21 @@ def test_update_dataset_tree_datasets_with_image_stacks(self):
11071058
]
11081059
self.assertListEqual(expected_calls, self.view.add_item_to_dataset_tree_widget.mock_calls)
11091060

1061+
def test_close_unused_visualisers(self):
1062+
stacks_ids = [uuid.uuid4() for _ in range(5)]
1063+
visualisers = {id: mock.Mock() for id in stacks_ids}
1064+
stacks = [mock.Mock(id=id) for id in stacks_ids]
1065+
self.presenter.stack_visualisers = visualisers
1066+
self.presenter.get_all_stacks = mock.Mock(return_value=stacks)
1067+
self.presenter._delete_stack_visualiser = mock.Mock()
1068+
1069+
self.presenter._close_unused_visualisers()
1070+
self.presenter._delete_stack_visualiser.assert_not_called()
1071+
1072+
stacks.pop(2)
1073+
self.presenter._close_unused_visualisers()
1074+
self.presenter._delete_stack_visualiser.assert_called_with(stacks_ids[2])
1075+
11101076

11111077
if __name__ == '__main__':
11121078
unittest.main()

0 commit comments

Comments
 (0)