From 47a56c2f19fd7b5dd6b781ab98f49eaa2e00c11e Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Sun, 8 Mar 2026 11:30:08 -0500 Subject: [PATCH 1/3] Allow opening statepoint files when one is already open --- openmc_plotter/main_window.py | 18 ++-- tests/test_statepoint_open_prompt.py | 140 +++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 tests/test_statepoint_open_prompt.py diff --git a/openmc_plotter/main_window.py b/openmc_plotter/main_window.py index 08fca3a..799aca2 100755 --- a/openmc_plotter/main_window.py +++ b/openmc_plotter/main_window.py @@ -582,15 +582,18 @@ def openView(self): self.loadViewFile(filename) def openStatePoint(self): - # check for an alread-open statepoint + # confirm replacement when a statepoint is already open if self.model.statepoint: - msg_box = QMessageBox() - msg_box.setText("Please close the current statepoint file before " - "opening a new one.") + msg_box = QMessageBox(self) + msg_box.setText("A statepoint file is currently open. Continue to " + "close it and open a new one?") msg_box.setIcon(QMessageBox.Information) - msg_box.setStandardButtons(QMessageBox.Ok) - msg_box.exec() - return + msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) + msg_box.setDefaultButton(QMessageBox.Cancel) + msg_box.button(QMessageBox.Yes).setText("Continue") + if msg_box.exec() != QMessageBox.Yes: + return + self.closeStatePoint() filename, ext = QFileDialog.getOpenFileName(self, "Open StatePoint", ".", "*.h5") if filename: @@ -635,6 +638,7 @@ def importProperties(self): def closeStatePoint(self): # remove the statepoint object and update the data menu filename = self.model.statepoint.filename + self.model.statepoint.close() self.model.statepoint = None self.model.currentView.selectedTally = None self.model.activeView.selectedTally = None diff --git a/tests/test_statepoint_open_prompt.py b/tests/test_statepoint_open_prompt.py new file mode 100644 index 0000000..51e71a0 --- /dev/null +++ b/tests/test_statepoint_open_prompt.py @@ -0,0 +1,140 @@ +from types import SimpleNamespace + +from openmc_plotter import main_window + + +class FakeButton: + def __init__(self): + self.text = None + + def setText(self, text): + self.text = text + + +class FakeMessageBox: + Yes = 1 + Cancel = 2 + Ok = 4 + Information = 8 + Warning = 16 + next_result = Cancel + instances = [] + + def __init__(self, *args, **kwargs): + self.text = None + self.icon = None + self.standard_buttons = None + self.default_button = None + self.buttons = {} + type(self).instances.append(self) + + def setText(self, text): + self.text = text + + def setIcon(self, icon): + self.icon = icon + + def setStandardButtons(self, buttons): + self.standard_buttons = buttons + + def setDefaultButton(self, button): + self.default_button = button + + def button(self, button): + return self.buttons.setdefault(button, FakeButton()) + + def exec(self): + return type(self).next_result + + +def make_window(events): + status_bar = SimpleNamespace( + showMessage=lambda message, timeout=None: events.append( + ("status", message, timeout) + ) + ) + statepoint = SimpleNamespace( + filename="current.h5", + close=lambda: events.append("close-current-statepoint"), + ) + model = SimpleNamespace( + statepoint=statepoint, + currentView=SimpleNamespace(selectedTally=17), + activeView=SimpleNamespace(selectedTally=17), + ) + + def open_statepoint(filename): + events.append(("open-statepoint", filename)) + model.statepoint = SimpleNamespace( + filename=filename, + close=lambda: events.append(("close-statepoint", filename)), + ) + + model.openStatePoint = open_statepoint + + window = SimpleNamespace( + model=model, + statusBar=lambda: status_bar, + updateDataMenu=lambda: events.append("update-data-menu"), + tallyPanel=SimpleNamespace( + selectTally=lambda: events.append("select-tally"), + update=lambda: events.append("update-tally-panel"), + ), + plotIm=SimpleNamespace(updatePixmap=lambda: events.append("update-pixmap")), + ) + window.closeStatePoint = lambda: main_window.MainWindow.closeStatePoint(window) + return window + + +def test_open_statepoint_cancel_keeps_current_file(monkeypatch): + events = [] + FakeMessageBox.instances = [] + FakeMessageBox.next_result = FakeMessageBox.Cancel + window = make_window(events) + + def fake_get_open_file_name(*args, **kwargs): + events.append("show-open-dialog") + return ("replacement.h5", "*.h5") + + monkeypatch.setattr(main_window, "QMessageBox", FakeMessageBox) + monkeypatch.setattr( + main_window.QFileDialog, "getOpenFileName", fake_get_open_file_name + ) + + main_window.MainWindow.openStatePoint(window) + + assert events == [] + assert window.model.statepoint.filename == "current.h5" + assert len(FakeMessageBox.instances) == 1 + assert FakeMessageBox.instances[0].text == ( + "A statepoint file is currently open. Continue to close it and open " + "a new one?" + ) + assert FakeMessageBox.instances[0].button(FakeMessageBox.Yes).text == "Continue" + + +def test_open_statepoint_continue_closes_current_file_before_reopening( + monkeypatch, +): + events = [] + FakeMessageBox.instances = [] + FakeMessageBox.next_result = FakeMessageBox.Yes + window = make_window(events) + + def fake_get_open_file_name(*args, **kwargs): + events.append("show-open-dialog") + return ("replacement.h5", "*.h5") + + monkeypatch.setattr(main_window, "QMessageBox", FakeMessageBox) + monkeypatch.setattr( + main_window.QFileDialog, "getOpenFileName", fake_get_open_file_name + ) + + main_window.MainWindow.openStatePoint(window) + + assert events.index("close-current-statepoint") < events.index("show-open-dialog") + assert ("open-statepoint", "replacement.h5") in events + assert ("status", "Opened statepoint file: replacement.h5", 5000) in events + assert window.model.currentView.selectedTally is None + assert window.model.activeView.selectedTally is None + assert window.model.statepoint.filename == "replacement.h5" From 736c694debd058e306c4834f479cec932da42387 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 9 Jul 2026 09:31:00 -0500 Subject: [PATCH 2/3] Remove test --- tests/test_statepoint_open_prompt.py | 140 --------------------------- 1 file changed, 140 deletions(-) delete mode 100644 tests/test_statepoint_open_prompt.py diff --git a/tests/test_statepoint_open_prompt.py b/tests/test_statepoint_open_prompt.py deleted file mode 100644 index 51e71a0..0000000 --- a/tests/test_statepoint_open_prompt.py +++ /dev/null @@ -1,140 +0,0 @@ -from types import SimpleNamespace - -from openmc_plotter import main_window - - -class FakeButton: - def __init__(self): - self.text = None - - def setText(self, text): - self.text = text - - -class FakeMessageBox: - Yes = 1 - Cancel = 2 - Ok = 4 - Information = 8 - Warning = 16 - next_result = Cancel - instances = [] - - def __init__(self, *args, **kwargs): - self.text = None - self.icon = None - self.standard_buttons = None - self.default_button = None - self.buttons = {} - type(self).instances.append(self) - - def setText(self, text): - self.text = text - - def setIcon(self, icon): - self.icon = icon - - def setStandardButtons(self, buttons): - self.standard_buttons = buttons - - def setDefaultButton(self, button): - self.default_button = button - - def button(self, button): - return self.buttons.setdefault(button, FakeButton()) - - def exec(self): - return type(self).next_result - - -def make_window(events): - status_bar = SimpleNamespace( - showMessage=lambda message, timeout=None: events.append( - ("status", message, timeout) - ) - ) - statepoint = SimpleNamespace( - filename="current.h5", - close=lambda: events.append("close-current-statepoint"), - ) - model = SimpleNamespace( - statepoint=statepoint, - currentView=SimpleNamespace(selectedTally=17), - activeView=SimpleNamespace(selectedTally=17), - ) - - def open_statepoint(filename): - events.append(("open-statepoint", filename)) - model.statepoint = SimpleNamespace( - filename=filename, - close=lambda: events.append(("close-statepoint", filename)), - ) - - model.openStatePoint = open_statepoint - - window = SimpleNamespace( - model=model, - statusBar=lambda: status_bar, - updateDataMenu=lambda: events.append("update-data-menu"), - tallyPanel=SimpleNamespace( - selectTally=lambda: events.append("select-tally"), - update=lambda: events.append("update-tally-panel"), - ), - plotIm=SimpleNamespace(updatePixmap=lambda: events.append("update-pixmap")), - ) - window.closeStatePoint = lambda: main_window.MainWindow.closeStatePoint(window) - return window - - -def test_open_statepoint_cancel_keeps_current_file(monkeypatch): - events = [] - FakeMessageBox.instances = [] - FakeMessageBox.next_result = FakeMessageBox.Cancel - window = make_window(events) - - def fake_get_open_file_name(*args, **kwargs): - events.append("show-open-dialog") - return ("replacement.h5", "*.h5") - - monkeypatch.setattr(main_window, "QMessageBox", FakeMessageBox) - monkeypatch.setattr( - main_window.QFileDialog, "getOpenFileName", fake_get_open_file_name - ) - - main_window.MainWindow.openStatePoint(window) - - assert events == [] - assert window.model.statepoint.filename == "current.h5" - assert len(FakeMessageBox.instances) == 1 - assert FakeMessageBox.instances[0].text == ( - "A statepoint file is currently open. Continue to close it and open " - "a new one?" - ) - assert FakeMessageBox.instances[0].button(FakeMessageBox.Yes).text == "Continue" - - -def test_open_statepoint_continue_closes_current_file_before_reopening( - monkeypatch, -): - events = [] - FakeMessageBox.instances = [] - FakeMessageBox.next_result = FakeMessageBox.Yes - window = make_window(events) - - def fake_get_open_file_name(*args, **kwargs): - events.append("show-open-dialog") - return ("replacement.h5", "*.h5") - - monkeypatch.setattr(main_window, "QMessageBox", FakeMessageBox) - monkeypatch.setattr( - main_window.QFileDialog, "getOpenFileName", fake_get_open_file_name - ) - - main_window.MainWindow.openStatePoint(window) - - assert events.index("close-current-statepoint") < events.index("show-open-dialog") - assert ("open-statepoint", "replacement.h5") in events - assert ("status", "Opened statepoint file: replacement.h5", 5000) in events - assert window.model.currentView.selectedTally is None - assert window.model.activeView.selectedTally is None - assert window.model.statepoint.filename == "replacement.h5" From d3b64b0e7f4e5ebb727ca4969ff7c88e99c94879 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 9 Jul 2026 13:09:11 -0500 Subject: [PATCH 3/3] Better handling of exception when loading statepoint --- openmc_plotter/main_window.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openmc_plotter/main_window.py b/openmc_plotter/main_window.py index 799aca2..aba50c7 100755 --- a/openmc_plotter/main_window.py +++ b/openmc_plotter/main_window.py @@ -599,17 +599,17 @@ def openStatePoint(self): if filename: try: self.model.openStatePoint(filename) - message = 'Opened statepoint file: {}' - except (FileNotFoundError, OSError): - message = 'Error opening statepoint file: {}' + message = f'Opened statepoint file: {filename}' + except Exception as e: + message = f'Error opening statepoint file: {filename}' msg_box = QMessageBox() - msg = "Could not open statepoint file: \n\n {} \n" - msg_box.setText(msg.format(filename)) + msg = f"Could not open statepoint file:\n{filename}\n\n{e}" + msg_box.setText(msg) msg_box.setIcon(QMessageBox.Warning) msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec() finally: - self.statusBar().showMessage(message.format(filename), 5000) + self.statusBar().showMessage(message, 5000) self.updateDataMenu() self.tallyPanel.update()