Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#2057: Updated the code to allow resetting the GUI to the default layout, including floating windows, through both the View menu and the right-click menu.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#1619: Updated the code to prevent the sidebar from minimising at all.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this file ended up in this PR by accident?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hussam-stfc I forgot to add the release notes for that issue when I closed it, so I snuck them in here. 👀

48 changes: 48 additions & 0 deletions mantidimaging/gui/windows/main/test/view_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,51 @@ def test_live_viewer_asks_for_data_dir_no_path(self, mock_show_live_viewer, mock
self.view.live_view_choose_directory()

mock_show_live_viewer.assert_not_called()

@mock.patch("mantidimaging.gui.windows.main.view.QMessageBox")
def test_reset_layout_no_stacks_shows_error(self, mock_qmb: mock.Mock):
# Make get_all_stacks return empty
self.view.get_all_stacks = mock.Mock(return_value=[]) # type: ignore[method-assign]

self.view.reset_layout()

mock_qmb.critical.assert_called_once_with(self.view, "Error", "No stacks detected.")

def test_reset_layout_recreates_stacks(self):
# Fake stacks to recreate
fake_stacks = [mock.Mock(), mock.Mock(), mock.Mock()]
self.view.get_all_stacks = mock.Mock(return_value=fake_stacks)
self.view.create_new_stack = mock.Mock()

# Mock existing stack_visualisers
old1, old2 = mock.Mock(), mock.Mock()
self.presenter.stack_visualisers = {"a": old1, "b": old2}

self.view.reset_layout()

# Close created stack visualisers
old1.close.assert_called_once()
old2.close.assert_called_once()

# stack_visualisers cleared
self.assertEqual(self.presenter.stack_visualisers, {})

# create_new_stack called for each new stack
self.assertEqual(self.view.create_new_stack.call_count, len(fake_stacks))
for stack in fake_stacks:
self.view.create_new_stack.assert_any_call(stack)

@mock.patch("mantidimaging.gui.windows.main.view.QMessageBox")
def test_reset_layout_handles_exceptions(self, mock_qmb: mock.Mock):
# Force an exception inside reset_layout
self.view.get_all_stacks = mock.Mock(side_effect=AttributeError("fail")) # type: ignore[method-assign]

self.view.reset_layout()

# Ensure critical error dialog is shown
mock_qmb.critical.assert_called_once()
args = mock_qmb.critical.call_args[0]
self.assertIn(
"Reset Layout could not completely reset the stacks",
args[2],
)
37 changes: 37 additions & 0 deletions mantidimaging/gui/windows/main/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class MainWindowView(BaseMainWindowView):
menuWorkflow: QMenu
menuImage: QMenu
menuHelp: QMenu
menuView: QMenu
menuTreeView: QMenu | None = None

actionRecon: QAction
Expand Down Expand Up @@ -118,6 +119,8 @@ class MainWindowView(BaseMainWindowView):
def __init__(self, open_dialogs: bool = True):
super().__init__(None, "gui/ui/main_window.ui")

self.menuView = self.menuBar().addMenu("View")

self.setWindowTitle("Mantid Imaging")

self.presenter = MainWindowPresenter(self)
Expand Down Expand Up @@ -252,6 +255,18 @@ def setup_shortcuts(self) -> None:

self.model_changed.connect(self.update_shortcuts)

self.actionResetLayout = QAction("Reset Layout", self)
self.actionResetLayout.setShortcut("Ctrl+Shift+R")
self.actionResetLayout.triggered.connect(self.reset_layout)

# Add Reset Layout action
self.actionResetLayout = QAction("Reset Layout", self)
self.actionResetLayout.setShortcut("Ctrl+Shift+R")
self.actionResetLayout.triggered.connect(self.reset_layout)

# Add to View menu (menuView always exists now)
self.menuView.addAction(self.actionResetLayout)

def populate_image_menu(self) -> None:
self.menuImage.clear()
current_stack = self.current_showing_stack()
Expand Down Expand Up @@ -813,3 +828,25 @@ def show_stack_properties_dialog(self, stack_id: uuid.UUID, origin_dataset: Data
assert stack is not None
stack_properties_dialog = StackPropertiesDialog(self, stack, origin_dataset, stack_data_type)
stack_properties_dialog.show()

def reset_layout(self):
"""Close and reopen all stacks, showing critical GUI errors for unsuccessful actions."""
try:
# Close all open stack windows
for vis in list(self.presenter.stack_visualisers.values()):
vis.close()
self.presenter.stack_visualisers.clear()

# Get all stacks
stacks = self.get_all_stacks()
if not stacks:
QMessageBox.critical(self, "Error", "No stacks detected.")
return

# Recreate all stacks
for stack in stacks:
self.create_new_stack(stack)

except (AttributeError, RuntimeError, TypeError) as unexpected_error:
QMessageBox.critical(self, "Error",
f"Reset Layout could not completely reset the stacks.\n\nDetails: {unexpected_error}")
3 changes: 2 additions & 1 deletion mantidimaging/gui/windows/stack_visualiser/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def __init__(self, parent: MainWindowView, images: ImageStack):
("Create sinograms from stack", lambda: self.presenter.notify(SVNotification.SWAP_AXES)),
("Set ROI", self.set_roi), ("Copy ROI to clipboard", self.copy_roi_to_clipboard), ("", None),
("Change window name", self.change_window_name_clicked),
("Goto projection", self.goto_projection), ("Goto angle", self.goto_angle)]
("Goto projection", self.goto_projection), ("Goto angle", self.goto_angle),
("Reset main window layout", self._main_window.reset_layout)]
self._context_actions = self.build_context_menu()

self.image_view = MIImageView(self)
Expand Down
Loading