def execute(): # all data will be row-major, so this needs to be specified as the default is col-major pyqtgraph.setConfigOptions(imageAxisOrder="row-major") # create the GUI event loop q_application = QApplication(sys.argv) q_application.setApplicationName("Mantid Imaging") application_window = MainWindowView() sys.excepthook = lambda exc_type, exc_value, exc_traceback: application_window.uncaught_exception( "".join(traceback.format_exception_only(exc_type, exc_value)), "".join( traceback.format_exception(exc_type, exc_value, exc_traceback))) def dont_let_qt_shutdown_while_debugging(type, value, tback): # log the exception here logging.getLogger(__name__).error( f"Exception {type} encountered:\n{traceback.format_exception(type, value, tback)}" ) # then call the default handler sys.__excepthook__(type, value, tback) if os.environ.get("PYDEVD_LOAD_VALUES_ASYNC", False): sys.excepthook = dont_let_qt_shutdown_while_debugging application_window.show() return sys.exit(q_application.exec_())
class EyesManager: def __init__(self, application_name="Mantid Imaging", test_name=None): self.application_name = application_name self.eyes = Eyes() self.eyes.match_level = MatchLevel.CONTENT self.image_directory = None self.imaging = None if test_name is None: test_name = self.application_name + " Tests" self.test_name = test_name def set_match_level(self, level: MatchLevel): self.eyes.match_level = level def set_batch(self, batch_id): batch_info = BatchInfo() batch_info.name = self.test_name batch_info.id = batch_id self.eyes.batch = batch_info def abort(self): self.eyes.abort_if_not_closed() def check_target(self, widget: QWidget = None): test_file_name = os.path.basename(inspect.stack()[2][1]) test_method_name = inspect.stack()[2][3] test_image_name = test_file_name.rpartition( ".")[0] + "_" + test_method_name image = self._take_screenshot(widget=widget, image_name=test_image_name) if self.eyes.api_key == "local": return if not self.eyes.is_open: self.eyes.open(self.application_name, test_file_name, dimension={ 'width': VIEWPORT_WIDTH, 'height': VIEWPORT_HEIGHT }) self.eyes.check_image(image, test_method_name) def close_imaging(self): self.imaging.close() def start_imaging(self): self.imaging = MainWindowView(open_dialogs=False) self.imaging.ask_to_use_closest_to_180 = mock.Mock(return_value=False) self.imaging.show() QApplication.processEvents() def _take_screenshot(self, widget: QWidget = None, image_name=None): """ :param widget: Widget to take screen shot of or main window if None. :param image_name: File name for screenshot :return: Will return the path to the saved image, or None if failed. """ if self.image_directory is None: directory = mkdtemp() else: directory = self.image_directory if widget is None and self.imaging is not None: widget = self.imaging if not isinstance(widget, QWidget): raise ValueError("widget is not a QWidget") time.sleep(0.2) QApplication.processEvents() window_image = widget.grab() if image_name is None: image_name = str(uuid4()) file_path = os.path.join(directory, image_name) + ".png" if window_image.save(file_path, "PNG"): return file_path else: raise IOError("Failed to save", file_path) def close_eyes(self): if self.eyes.is_open: self.eyes.close()
class GuiSystemBase(unittest.TestCase): app: QApplication def setUp(self) -> None: self.main_window = MainWindowView() self.main_window.show() QTest.qWait(SHORT_DELAY) def tearDown(self) -> None: QTimer.singleShot(SHORT_DELAY, lambda: self._click_messageBox("Yes")) self.main_window.close() QTest.qWait(SHORT_DELAY) @classmethod def _click_messageBox(cls, button_text: str): """Needs to be queued with QTimer.singleShot before triggering the message box""" for widget in cls.app.topLevelWidgets(): if isinstance(widget, QMessageBox) and widget.isVisible(): for button in widget.buttons(): if button.text().replace("&", "") == button_text: QTest.mouseClick(button, Qt.LeftButton) return button_texts = [button.text() for button in widget.buttons()] raise ValueError( f"Could not find button '{button_text}' in {button_texts}.\n" f"Message box: {widget.windowTitle()} {widget.text()}") @classmethod def _click_InputDialog(cls, set_int: Optional[int] = None): """Needs to be queued with QTimer.singleShot before triggering the message box""" for widget in cls.app.topLevelWidgets(): if isinstance(widget, QInputDialog) and widget.isVisible(): if set_int: widget.setIntValue(set_int) QTest.qWait(SHORT_DELAY) widget.accept() def _close_welcome(self): self.main_window.welcome_window.view.close() @classmethod def _wait_until(cls, test_func: Callable[[], bool], delay=0.1, max_retry=100): """ Repeat test_func every delay seconds until is becomes true. Or if max_retry is reached return false. """ for _ in range(max_retry): if test_func(): return True QTest.qWait(int(delay * 1000)) raise RuntimeError("_wait_until reach max retries") @classmethod def _wait_for_widget_visible(cls, widget_type, delay=0.1, max_retry=100): for _ in range(max_retry): for widget in cls.app.topLevelWidgets(): if isinstance(widget, widget_type) and widget.isVisible(): return True QTest.qWait(delay * 1000) raise RuntimeError("_wait_for_stack_selector reach max retries") @mock.patch( "mantidimaging.gui.windows.load_dialog.view.MWLoadDialog.select_file") def _load_data_set(self, mocked_select_file): mocked_select_file.return_value = LOAD_SAMPLE initial_stacks = len( self.main_window.presenter.get_active_stack_visualisers()) def test_func() -> bool: current_stacks = len( self.main_window.presenter.get_active_stack_visualisers()) return (current_stacks - initial_stacks) >= 5 self.main_window.actionLoadDataset.trigger() QTest.qWait(SHOW_DELAY) self.main_window.load_dialogue.presenter.notify( Notification.UPDATE_ALL_FIELDS) QTest.qWait(SHOW_DELAY) self.main_window.load_dialogue.accept() self._wait_until(test_func, max_retry=600) def _open_operations(self): self.main_window.actionFilters.trigger() def _open_reconstruction(self): self.main_window.actionRecon.trigger() def _close_stack_tabs(self): while self.main_window.dataset_tree_widget.topLevelItemCount(): self.main_window.dataset_tree_widget.topLevelItem(0).setSelected( True) self.main_window._delete_container()