diff --git a/rascal2/dialogs/project_dialog.py b/rascal2/dialogs/project_dialog.py index 926f73c..24c86f8 100644 --- a/rascal2/dialogs/project_dialog.py +++ b/rascal2/dialogs/project_dialog.py @@ -1,5 +1,4 @@ import os -from typing import Literal from PyQt6 import QtCore, QtGui, QtWidgets @@ -38,11 +37,10 @@ def __init__(self, parent): self.setModal(True) self.setMinimumWidth(700) - self.name_error = False - self.folder_error = False + self.folder_path = "" self.create_buttons() - self.create_labels() + self.create_form() self.add_widgets_to_layout() def add_widgets_to_layout(self) -> None: @@ -79,11 +77,11 @@ def add_widgets_to_layout(self) -> None: self.setLayout(self.main_layout) - def create_labels(self) -> None: + def create_form(self) -> None: """ - Create labels. + Create form widgets. """ - # Project name labels + # Project name widgets self.project_name_label = QtWidgets.QLabel("Project Name:", self) self.project_name_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft) self.project_name_label.setStyleSheet(self._label_style) @@ -95,7 +93,7 @@ def create_labels(self) -> None: self.project_name_error.setStyleSheet(self._error_style) self.project_name_error.hide() - # Project folder labels + # Project folder widgets self.project_folder_label = QtWidgets.QLabel("Project Folder:", self) self.project_folder_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft) self.project_folder_label.setStyleSheet(self._label_style) @@ -131,79 +129,58 @@ def cancel_project_creation(self) -> None: """ Cancel project creation. """ - self.close() if self.parent().new_project_action_called else self.parent().toggleView() - self.reset_variables() - - def reset_variables(self) -> None: - """ - Reset variables. - """ + self.close() if self.parent().toolbar.isEnabled() else self.parent().toggleView() self.project_folder.setText("") self.project_name.setText("") - self.name_error = False - self.folder_error = False def open_folder_selector(self) -> None: """ Open folder selector. """ - folder_path = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Folder") - if folder_path: - files_in_folder = list(filter(lambda x: x[0] != ".", os.listdir(folder_path))) - if files_in_folder: - self.handle_error("folder") - else: - self.handle_success("folder") - self.project_folder.setText(folder_path) - - def handle_error(self, tag: Literal["name", "folder"]) -> None: - """ - Display error msgs. - - Parameters - ---------- - tag: Literal["name", "folder"] - Specifies whether to show the error - for the project name or folder. - """ - getattr(self, "project_" + tag).setStyleSheet(self._line_edit_error_style) - getattr(self, "project_" + tag + "_error").show() - setattr(self, tag + "_error", True) + self.folder_path = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Folder") + if self.folder_path: + self.verify_folder() - def handle_success(self, tag: Literal["name", "folder"]) -> None: + def verify_folder(self) -> None: """ - Hide error msgs. - - Parameters - ---------- - tag: Literal["name", "folder"] - Specifies whether to clear the error - for the project name or folder. + Verify the project folder. """ - getattr(self, "project_" + tag).setStyleSheet("") - getattr(self, "project_" + tag + "_error").hide() - setattr(self, tag + "_error", False) + error = False + if self.folder_path: + files_in_folder = [file for file in os.listdir(self.folder_path) if not file.startswith(".")] + if files_in_folder: + error = True + elif self.project_folder.text() == "": + error = True + + if error: + self.project_folder.setStyleSheet(self._line_edit_error_style) + self.project_folder_error.show() + self.project_folder.setText("") + else: + self.project_folder.setStyleSheet("") + self.project_folder_error.hide() + self.project_folder.setText(self.folder_path) - def verify_inputs(self, tag: Literal["name", "folder"]) -> None: + def verify_name(self) -> None: """ - Verify inputs. - - Parameters - ---------- - tag: Literal["name", "folder"] - Specifies whether the input verification - is for the project name or folder. + Verify the project name. """ - if getattr(self, "project_" + tag).text() == "": - self.handle_error(tag) + if self.project_name.text() == "": + self.project_name.setStyleSheet(self._line_edit_error_style) + self.project_name_error.show() else: - self.handle_success(tag) + self.project_name.setStyleSheet("") + self.project_name_error.hide() def create_project(self) -> None: """ Create project if inputs verified. """ - self.verify_inputs("name") - self.verify_inputs("folder") - if not self.name_error and not self.folder_error: - self.parent().createNewProject() + self.verify_name() + self.verify_folder() + if self.project_name_error.isHidden() and self.project_folder_error.isHidden(): + self.parent().presenter.createProject(self.project_name.text(), self.project_folder.text()) + if not self.parent().toolbar.isEnabled(): + self.parent().toolbar.setEnabled(True) + self.close() diff --git a/rascal2/ui/presenter.py b/rascal2/ui/presenter.py index 8b1a625..e30ea15 100644 --- a/rascal2/ui/presenter.py +++ b/rascal2/ui/presenter.py @@ -26,5 +26,6 @@ def createProject(self, name: str, save_path: str): """ self.model.createProject(name, save_path) + self.view.setWindowTitle(name) # TODO if the view's central widget is the startup one then setup MDI else reset the widgets. self.view.setupMDI() diff --git a/rascal2/ui/view.py b/rascal2/ui/view.py index a1407b5..ee294fc 100644 --- a/rascal2/ui/view.py +++ b/rascal2/ui/view.py @@ -45,7 +45,6 @@ def __init__(self): def showProjectDialog(self): """Shows the dialog to create a new rascal project""" - self.new_project_action_called = True self.project_dlg = ProjectDialog(self) self.project_dlg.show() @@ -54,15 +53,6 @@ def toggleView(self): self.startup_dlg.hide() if self.startup_dlg.isVisible() else self.startup_dlg.show() self.project_dlg.hide() if self.project_dlg.isVisible() else self.project_dlg.show() - def createNewProject(self): - """Creates a new rascal project""" - self.setWindowTitle(MAIN_WINDOW_TITLE + " - " + self.project_dlg.project_name.text()) - if not self.new_project_action_called: - self.startup_dlg.close() - self.project_dlg.close() - self.toolbar.setEnabled(True) - self.setupMDI() - def createActions(self): """Creates the menu and toolbar actions""" @@ -70,7 +60,6 @@ def createActions(self): self.new_project_action.setStatusTip("Create a new project") self.new_project_action.setIcon(QtGui.QIcon(path_for("new-project.png"))) self.new_project_action.triggered.connect(self.showProjectDialog) - self.new_project_action_called = False self.new_project_action.setShortcut(QtGui.QKeySequence.StandardKey.New) self.open_project_action = QtGui.QAction("&Open", self) diff --git a/rascal2/widgets/startup_widget.py b/rascal2/widgets/startup_widget.py index b9aeb2e..fbb3162 100644 --- a/rascal2/widgets/startup_widget.py +++ b/rascal2/widgets/startup_widget.py @@ -107,6 +107,6 @@ def create_labels(self) -> None: self.import_project_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.import_project_label.setStyleSheet(self._label_style) - self.import_r1_label = QtWidgets.QLabel("Import R1\nProject", self) + self.import_r1_label = QtWidgets.QLabel("Import RasCAL-1\nProject", self) self.import_r1_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.import_r1_label.setStyleSheet(self._label_style) diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index c5ed412..1593c2c 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -1,13 +1,22 @@ from unittest.mock import patch import pytest -from PyQt6 import QtCore, QtTest, QtWidgets +from PyQt6 import QtCore, QtWidgets from rascal2.dialogs.project_dialog import ProjectDialog +class MockPresenter(QtWidgets.QMainWindow): + def createProject(self, name: str, folder: str): + pass + + class MockParentWindow(QtWidgets.QMainWindow): - new_project_action_called = False + def __init__(self): + super().__init__() + self.presenter = MockPresenter() + self.toolbar = self.addToolBar("ToolBar") + self.toolbar.setEnabled(False) def toggleView(self): pass @@ -53,55 +62,55 @@ def test_project_dialog_initial_state(setup_project_dialog_widget): assert project_dialog.project_folder_error.isHidden() assert project_dialog.project_folder.isReadOnly() - assert not project_dialog.name_error - assert not project_dialog.folder_error - - -def test_inline_error_msgs(setup_project_dialog_widget): +@patch("os.listdir") +def test_inline_error_msgs(mock_listdir, setup_project_dialog_widget): """ Tests the project name and folder inline errors. """ project_dialog, _ = setup_project_dialog_widget + mock_listdir.return_value = [".hiddenfile"] + # tests the project name and folder line edit errors displayed. - QtTest.QTest.mouseClick(project_dialog.create_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.create_button.click() assert not project_dialog.project_name_error.isHidden() - assert project_dialog.name_error assert not project_dialog.project_folder_error.isHidden() - assert project_dialog.folder_error # tests the project name line edit error displayed. project_dialog.project_folder.setText("test-folder") - QtTest.QTest.mouseClick(project_dialog.create_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.folder_path = "test-folder" + project_dialog.create_button.click() assert not project_dialog.project_name_error.isHidden() - assert project_dialog.name_error assert project_dialog.project_folder_error.isHidden() - assert not project_dialog.folder_error # tests the project folder line edit error displayed. project_dialog.project_name.setText("test-name") project_dialog.project_folder.setText("") - QtTest.QTest.mouseClick(project_dialog.create_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.folder_path = "" + project_dialog.create_button.click() assert project_dialog.project_name_error.isHidden() - assert not project_dialog.name_error assert not project_dialog.project_folder_error.isHidden() - assert project_dialog.folder_error - -@patch.object(MockParentWindow, "createNewProject") -def test_create_button(mock_create_new_project, setup_project_dialog_widget): +@patch("os.listdir") +@patch.object(MockPresenter, "createProject") +def test_create_button(mock_create_new_project, mock_listdir, setup_project_dialog_widget): """ Tests create button on the ProjectDialog class. """ project_dialog, _ = setup_project_dialog_widget + mock_listdir.return_value = [] + project_dialog.project_name.setText("test-name") project_dialog.project_folder.setText("test-folder") - QtTest.QTest.mouseClick(project_dialog.create_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.folder_selected = True + project_dialog.folder_path = "test-folder" + project_dialog.create_button.click() mock_create_new_project.assert_called_once() + assert project_dialog.parent().toolbar.isEnabled() @patch.object(MockParentWindow, "toggleView") @@ -112,18 +121,14 @@ def test_cancel_button(mock_toggle_view, setup_project_dialog_widget): project_dialog, _ = setup_project_dialog_widget project_dialog.project_name.setText("test-name") - project_dialog.name_error = True project_dialog.project_folder.setText("test-folder") - project_dialog.folder_error = True - QtTest.QTest.mouseClick(project_dialog.cancel_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.cancel_button.click() mock_toggle_view.assert_called_once() assert project_dialog.project_name.text() == "" - assert not project_dialog.name_error assert project_dialog.project_folder.text() == "" - assert not project_dialog.folder_error @patch("os.listdir") @@ -138,17 +143,15 @@ def test_browse_button(mock_get_existing_directory, mock_listdir, setup_project_ mock_get_existing_directory.return_value = "/test/folder/path" mock_listdir.return_value = [".hiddenfile"] - QtTest.QTest.mouseClick(project_dialog.browse_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.browse_button.click() assert project_dialog.project_folder.text() == "/test/folder/path" - assert not project_dialog.folder_error - - project_dialog.reset_variables() + assert project_dialog.project_folder_error.isHidden() # When a non empty folder is selected. mock_listdir.return_value = [".hiddenfile", "testfile"] - QtTest.QTest.mouseClick(project_dialog.browse_button, QtCore.Qt.MouseButton.LeftButton) + project_dialog.browse_button.click() assert project_dialog.project_folder.text() == "" - assert project_dialog.folder_error + assert not project_dialog.project_folder_error.isHidden() diff --git a/tests/test_widgets.py b/tests/test_widgets.py index ddf4dd0..db9c6d6 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1,7 +1,6 @@ from unittest.mock import patch import pytest -from PyQt6 import QtCore, QtTest from rascal2.widgets.startup_widget import StartUpWidget from tests.test_dialogs import MockParentWindow @@ -25,7 +24,7 @@ def test_startup_widget_initial_state(setup_startup_widget): assert startup_widget.new_project_label.text() == "New\nProject" assert startup_widget.import_project_label.text() == "Import Existing\nProject" - assert startup_widget.import_r1_label.text() == "Import R1\nProject" + assert startup_widget.import_r1_label.text() == "Import RasCAL-1\nProject" @patch.object(MockParentWindow, "toggleView") @@ -34,5 +33,5 @@ def test_toggle_view_called(mock, setup_startup_widget): Tests the toggleView method is called once. """ startup_widget, _ = setup_startup_widget - QtTest.QTest.mouseClick(startup_widget.new_project_button, QtCore.Qt.MouseButton.LeftButton) + startup_widget.new_project_button.click() mock.assert_called_once()