From 1dddad477b249a1f12566641bf8b032bb991478d Mon Sep 17 00:00:00 2001 From: Christina Bukas Date: Mon, 18 Dec 2023 11:52:50 +0100 Subject: [PATCH 1/6] updated create_warning_box to include cancel option --- src/client/dcp_client/gui/main_window.py | 6 +++--- src/client/dcp_client/gui/napari_window.py | 8 ++++---- src/client/dcp_client/gui/welcome_window.py | 6 +++--- src/client/dcp_client/utils/utils.py | 11 ++++++++--- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/client/dcp_client/gui/main_window.py b/src/client/dcp_client/gui/main_window.py index 76264d4..d4463c6 100644 --- a/src/client/dcp_client/gui/main_window.py +++ b/src/client/dcp_client/gui/main_window.py @@ -132,11 +132,11 @@ def on_item_inprogr_selected(self, item): def on_train_button_clicked(self): message_text = self.app.run_train() - create_warning_box(message_text) + _ = create_warning_box(message_text) def on_run_inference_button_clicked(self): message_text, message_title = self.app.run_inference() - create_warning_box(message_text, message_title) + _ = create_warning_box(message_text, message_title) def on_launch_napari_button_clicked(self): ''' @@ -144,7 +144,7 @@ def on_launch_napari_button_clicked(self): ''' if not self.app.cur_selected_img or '_seg.tiff' in self.app.cur_selected_img: message_text = "Please first select an image you wish to visualise. The selected image must be an original images, not a mask." - create_warning_box(message_text, message_title="Warning") + _ = create_warning_box(message_text, message_title="Warning") else: self.nap_win = NapariWindow(self.app) self.nap_win.show() diff --git a/src/client/dcp_client/gui/napari_window.py b/src/client/dcp_client/gui/napari_window.py index ecef6a8..e0451dd 100644 --- a/src/client/dcp_client/gui/napari_window.py +++ b/src/client/dcp_client/gui/napari_window.py @@ -56,7 +56,7 @@ def on_add_to_curated_button_clicked(self): ''' if self.app.cur_selected_path == str(self.app.train_data_path): message_text = "Image is already in the \'Curated data\' folder and should not be changed again" - utils.create_warning_box(message_text, message_title="Warning") + _ = utils.create_warning_box(message_text, message_title="Warning") return # take the name of the currently selected layer (by the user) @@ -64,7 +64,7 @@ def on_add_to_curated_button_clicked(self): # TODO if more than one item is selected this will break! if '_seg' not in cur_seg_selected: message_text = "Please select the segmenation you wish to save from the layer list" - utils.create_warning_box(message_text, message_title="Warning") + _ = utils.create_warning_box(message_text, message_title="Warning") return seg = self.viewer.layers[cur_seg_selected].data @@ -87,7 +87,7 @@ def on_add_to_inprogress_button_clicked(self): # TODO: Do we allow this? What if they moved it by mistake? User can always manually move from their folders?) if self.app.cur_selected_path == str(self.app.train_data_path): message_text = "Images from '\Curated data'\ folder can not be moved back to \'Curatation in progress\' folder." - utils.create_warning_box(message_text, message_title="Warning") + _ = utils.create_warning_box(message_text, message_title="Warning") return # take the name of the currently selected layer (by the user) @@ -95,7 +95,7 @@ def on_add_to_inprogress_button_clicked(self): # TODO if more than one item is selected this will break! if '_seg' not in cur_seg_selected: message_text = "Please select the segmenation you wish to save from the layer list" - utils.create_warning_box(message_text, message_title="Warning") + _ = utils.create_warning_box(message_text, message_title="Warning") return # Move original image diff --git a/src/client/dcp_client/gui/welcome_window.py b/src/client/dcp_client/gui/welcome_window.py index 8886d7e..5c385a5 100644 --- a/src/client/dcp_client/gui/welcome_window.py +++ b/src/client/dcp_client/gui/welcome_window.py @@ -129,15 +129,15 @@ def start_main(self): self.mw = MainWindow(self.app) else: message_text = "You need to specify a folder both for your uncurated and curated dataset (even if the curated folder is currently empty). Please go back and select folders for both." - create_warning_box(message_text, message_title="Warning") + _ = create_warning_box(message_text, message_title="Warning") def start_upload(self): message_text = ("Your current configurations are set to run some operations on the cloud. \n" "For this we need to upload your data to our server." "We will now upload your data. Click ok to continue. \n" "If you do not agree close the application and contact your software provider.") - create_warning_box(message_text, message_title="Warning") - self.app.upload_data_to_server() + usr_response = create_warning_box(message_text, message_title="Warning", add_cancel_btn=True) + if usr_response: self.app.upload_data_to_server() self.hide() self.mw = MainWindow(self.app) \ No newline at end of file diff --git a/src/client/dcp_client/utils/utils.py b/src/client/dcp_client/utils/utils.py index c7ca5bf..594712d 100644 --- a/src/client/dcp_client/utils/utils.py +++ b/src/client/dcp_client/utils/utils.py @@ -23,13 +23,18 @@ def icon(self, type: 'QFileIconProvider.IconType'): else: return super().icon(type) -def create_warning_box(message_text, message_title="Warning"): +def create_warning_box(message_text, message_title="Warning", add_cancel_btn=False): msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText(message_text) msg.setWindowTitle(message_title) - msg.setStandardButtons(QMessageBox.Ok) - msg.exec() + if add_cancel_btn: + msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) + else: + msg.setStandardButtons(QMessageBox.Ok) + usr_response = msg.exec() + if usr_response == QMessageBox.Ok: return True + else: return False def read_config(name, config_path = 'config.cfg') -> dict: """Reads the configuration file From fb8d55b9d7f1fe8307b402bfe70b3d8f2330b643 Mon Sep 17 00:00:00 2001 From: Christina Bukas Date: Tue, 19 Dec 2023 10:20:54 +0100 Subject: [PATCH 2/6] adding some unit tests --- src/client/dcp_client/utils/utils.py | 5 ++- src/client/test/test_utils.py | 67 ++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/client/test/test_utils.py diff --git a/src/client/dcp_client/utils/utils.py b/src/client/dcp_client/utils/utils.py index 594712d..bcf5709 100644 --- a/src/client/dcp_client/utils/utils.py +++ b/src/client/dcp_client/utils/utils.py @@ -23,15 +23,18 @@ def icon(self, type: 'QFileIconProvider.IconType'): else: return super().icon(type) -def create_warning_box(message_text, message_title="Warning", add_cancel_btn=False): +def create_warning_box(message_text, message_title="Warning", add_cancel_btn=False): + #setup box msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText(message_text) msg.setWindowTitle(message_title) + # if specified add a cancel button else only an ok if add_cancel_btn: msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) else: msg.setStandardButtons(QMessageBox.Ok) + # return if user clicks Ok and False otherwise usr_response = msg.exec() if usr_response == QMessageBox.Ok: return True else: return False diff --git a/src/client/test/test_utils.py b/src/client/test/test_utils.py new file mode 100644 index 0000000..b64c534 --- /dev/null +++ b/src/client/test/test_utils.py @@ -0,0 +1,67 @@ +import sys +sys.path.append("../") +#from qtpy.QtTest import QTest +#from qtpy.QtWidgets import QMessageBox +#from qtpy.QtCore import Qt +from dcp_client.utils import utils + +def test_get_relative_path(): + filepath = '/here/we/are/testing/something.txt' + assert utils.get_relative_path(filepath)== 'something.txt' + +def test_get_path_stem(): + filepath = '/here/we/are/testing/something.txt' + assert utils.get_path_stem(filepath)== 'something' + +def test_get_path_name(): + filepath = '/here/we/are/testing/something.txt' + assert utils.get_path_name(filepath)== 'something.txt' + +def test_get_path_parent(): + filepath = '/here/we/are/testing/something.txt' + assert utils.get_path_parent(filepath)== '/here/we/are/testing' + +def test_join_path(): + filepath = '/here/we/are/testing/something.txt' + path1 = '/here/we/are/testing' + path2 = 'something.txt' + assert utils.join_path(path1, path2) == filepath + +''' +def test_create_warning_box_ok(qtbot): + # Using qtbot fixture provided by pytest-qt + result = None + + def execute_warning_box(): + nonlocal result + box = QMessageBox() + while result is None: + result = utils.create_warning_box("Test Message", custom_dialog=box) + # Close the message box by simulating a click on the OK button + QTest.mouseClick(box.button(QMessageBox.Ok), Qt.LeftButton) + + # Run the function in the main thread using qtbot + #qtbot.addWidget(box) # Add the QMessageBox widget to the QtBot's widget registry + qtbot.waitUntil(execute_warning_box, timeout=5000) #lambda: result is not None) + + assert result is True # Assert the expected result + + +def test_create_warning_box_cancel(qtbot): + # Using qtbot fixture provided by pytest-qt + result = None + + def execute_warning_box(): + nonlocal result + result = utils.create_warning_box("Test Message", add_cancel_btn=True) + # Close the message box by simulating a click on the Cancel button + QTest.mouseClick(box, Qt.LeftButton) + + box = QMessageBox() + box.button(QMessageBox.Cancel) + # Run the function in the main thread using qtbot + qtbot.addWidget(box) # Add the QMessageBox widget to the QtBot's widget registry + qtbot.waitUntil(execute_warning_box) # Add a timeout for the function to execute + + assert result is False # Assert the expected result +''' \ No newline at end of file From 12896a6336fdd1150bbbeb2a778371cd059f7cc4 Mon Sep 17 00:00:00 2001 From: Christina Bukas Date: Tue, 19 Dec 2023 11:04:25 +0100 Subject: [PATCH 3/6] added test for create_warning_box --- src/client/dcp_client/utils/utils.py | 11 ++++++-- src/client/test/test_utils.py | 41 +++++++--------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/client/dcp_client/utils/utils.py b/src/client/dcp_client/utils/utils.py index bcf5709..1bf1bc7 100644 --- a/src/client/dcp_client/utils/utils.py +++ b/src/client/dcp_client/utils/utils.py @@ -1,5 +1,5 @@ from PyQt5.QtWidgets import QFileIconProvider, QMessageBox -from PyQt5.QtCore import QSize +from PyQt5.QtCore import QSize, QTimer from PyQt5.QtGui import QPixmap, QIcon from pathlib import Path, PurePath @@ -23,17 +23,22 @@ def icon(self, type: 'QFileIconProvider.IconType'): else: return super().icon(type) -def create_warning_box(message_text, message_title="Warning", add_cancel_btn=False): +def create_warning_box(message_text, message_title="Warning", add_cancel_btn=False, custom_dialog=None, sim=False): #setup box - msg = QMessageBox() + if custom_dialog is None: msg = QMessageBox() + else: msg = custom_dialog msg.setIcon(QMessageBox.Information) msg.setText(message_text) msg.setWindowTitle(message_title) # if specified add a cancel button else only an ok if add_cancel_btn: msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) + # simulate button click if specified - workaround used for testing + if sim: QTimer.singleShot(0, msg.button(QMessageBox.Cancel).clicked) else: msg.setStandardButtons(QMessageBox.Ok) + # simulate button click if specified - workaround used for testing + if sim: QTimer.singleShot(0, msg.button(QMessageBox.Ok).clicked) # return if user clicks Ok and False otherwise usr_response = msg.exec() if usr_response == QMessageBox.Ok: return True diff --git a/src/client/test/test_utils.py b/src/client/test/test_utils.py index b64c534..22e3b26 100644 --- a/src/client/test/test_utils.py +++ b/src/client/test/test_utils.py @@ -1,8 +1,8 @@ import sys sys.path.append("../") -#from qtpy.QtTest import QTest -#from qtpy.QtWidgets import QMessageBox -#from qtpy.QtCore import Qt +from qtpy.QtTest import QTest +from qtpy.QtWidgets import QMessageBox +from qtpy.QtCore import Qt, QTimer from dcp_client.utils import utils def test_get_relative_path(): @@ -27,41 +27,20 @@ def test_join_path(): path2 = 'something.txt' assert utils.join_path(path1, path2) == filepath -''' def test_create_warning_box_ok(qtbot): - # Using qtbot fixture provided by pytest-qt result = None - def execute_warning_box(): nonlocal result box = QMessageBox() - while result is None: - result = utils.create_warning_box("Test Message", custom_dialog=box) - # Close the message box by simulating a click on the OK button - QTest.mouseClick(box.button(QMessageBox.Ok), Qt.LeftButton) - - # Run the function in the main thread using qtbot - #qtbot.addWidget(box) # Add the QMessageBox widget to the QtBot's widget registry - qtbot.waitUntil(execute_warning_box, timeout=5000) #lambda: result is not None) - - assert result is True # Assert the expected result - + result = utils.create_warning_box("Test Message", custom_dialog=box, sim=True) + qtbot.waitUntil(execute_warning_box, timeout=5000) + assert result is True def test_create_warning_box_cancel(qtbot): - # Using qtbot fixture provided by pytest-qt result = None - def execute_warning_box(): nonlocal result - result = utils.create_warning_box("Test Message", add_cancel_btn=True) - # Close the message box by simulating a click on the Cancel button - QTest.mouseClick(box, Qt.LeftButton) - - box = QMessageBox() - box.button(QMessageBox.Cancel) - # Run the function in the main thread using qtbot - qtbot.addWidget(box) # Add the QMessageBox widget to the QtBot's widget registry - qtbot.waitUntil(execute_warning_box) # Add a timeout for the function to execute - - assert result is False # Assert the expected result -''' \ No newline at end of file + box = QMessageBox() + result = utils.create_warning_box("Test Message", add_cancel_btn=True, custom_dialog=box, sim=True) + qtbot.waitUntil(execute_warning_box, timeout=5000) # Add a timeout for the function to execute + assert result is False \ No newline at end of file From 1348d85e5fb43aa022d96698164d237d06375f02 Mon Sep 17 00:00:00 2001 From: Christina Bukas <31160776+christinab12@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:13:15 +0100 Subject: [PATCH 4/6] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d79f3d..2d4640b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,7 @@ jobs: python -m pip install --upgrade pip python -m pip install setuptools pip install pytest + pip install pytest-qt pip install coverage pip install -e ".[testing]" working-directory: src/client From 144d3e8a8de150cc74d18068a28ab348cdd8d995 Mon Sep 17 00:00:00 2001 From: Christina Bukas <31160776+christinab12@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:12:36 +0100 Subject: [PATCH 5/6] Update test.yml --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d4640b..38e1f80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,8 @@ jobs: matrix: platform: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.8, 3.9, "3.10"] - + env: + DISPLAY: ':99.0' steps: - name: Checkout Repository uses: actions/checkout@v3 From c5fc8996b6a32d9cc2b7491a0aa1e502cf4bb42a Mon Sep 17 00:00:00 2001 From: Christina Bukas <31160776+christinab12@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:30:39 +0100 Subject: [PATCH 6/6] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 38e1f80..423d425 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,6 +46,7 @@ jobs: python -m pip install setuptools pip install pytest pip install pytest-qt + pip install pytest-xvfb pip install coverage pip install -e ".[testing]" working-directory: src/client