From d96eac4c2766d2b2f772d1da69e571db293031fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20K=C5=82apyta?= <33362868+krystofair@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:23:09 +0200 Subject: [PATCH 1/5] Add ext to filename when user forget it when rename This is one solution among two I think. The second one is change visibility of files the user can choose from when opening saved workflow. The first is simpler I guess and it seems more reasonable. Possibility about change type of `filename` when passed as `pathlib.Path`, but this operation here is safe. Casting will not raise exception when `filename` is `pathlib.Path` type indeed. --- orangecanvas/application/canvasmain.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/orangecanvas/application/canvasmain.py b/orangecanvas/application/canvasmain.py index 1269127e..b25f8074 100644 --- a/orangecanvas/application/canvasmain.py +++ b/orangecanvas/application/canvasmain.py @@ -1573,6 +1573,9 @@ def save_scheme_to(self, scheme, filename): `True`, else show a message to the user explaining the error and return `False`. """ + # Enforcing ".ows" extension during saving file. + # see .../biolab/orange-canvas-core/pull/330 + filename = filename if str(filename).endswith('.ows') else f"{filename}.ows" dirname, basename = os.path.split(filename) title = scheme.title or "untitled" From 7614d0fa862ecaedb65a9e0cbce42d5e13bb2d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20K=C5=82apyta?= <33362868+krystofair@users.noreply.github.com> Date: Sat, 2 Aug 2025 00:09:35 +0200 Subject: [PATCH 2/5] Saving only when user click OK. --- orangecanvas/application/canvasmain.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/orangecanvas/application/canvasmain.py b/orangecanvas/application/canvasmain.py index b25f8074..19ff5d17 100644 --- a/orangecanvas/application/canvasmain.py +++ b/orangecanvas/application/canvasmain.py @@ -1551,11 +1551,12 @@ def save_scheme_as(self): objectName="save-as-ows-filedialog", ) dialog.setNameFilter(self.tr("Orange Workflow (*.ows)")) - dialog.exec() - files = dialog.selectedFiles() + # `deleteLater` can be ivoked before `exec` as PyQt 6.9 doc says, that + # it is activated after `exec`, and work on PyQt 5 version well (here) dialog.deleteLater() - if files: - filename = files[0] + # dialog.exec waits for user action + if dialog.exec(): + filename = dialog.selectedFiles()[0] settings.setValue("last-scheme-dir", os.path.dirname(filename)) if self.save_scheme_to(curr_scheme, filename): document.setPath(filename) @@ -1594,7 +1595,7 @@ def save_scheme_to(self, scheme, filename): exc_info=True, parent=self ) - return False + return False # return False here because there is second part try: with open(filename, "wb") as f: @@ -1611,7 +1612,6 @@ def save_scheme_to(self, scheme, filename): informative_text=self.tr("Choose another location."), parent=self ) - return False except PermissionError as ex: log.error("%s saving '%s'", type(ex).__name__, filename, exc_info=True) @@ -1624,7 +1624,6 @@ def save_scheme_to(self, scheme, filename): "another location."), parent=self ) - return False except OSError as ex: log.error("%s saving '%s'", type(ex).__name__, filename, exc_info=True) @@ -1635,8 +1634,6 @@ def save_scheme_to(self, scheme, filename): exc_info=True, parent=self ) - return False - except Exception: # pylint: disable=broad-except log.error("Error saving %r to %r", scheme, filename, exc_info=True) message_critical( @@ -1646,7 +1643,7 @@ def save_scheme_to(self, scheme, filename): exc_info=True, parent=self ) - return False + return False # default behaviour def save_swp(self): """ From b71317b05739655fa1c7f4af8b537fc9cd06eaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20K=C5=82apyta?= <33362868+krystofair@users.noreply.github.com> Date: Fri, 22 Aug 2025 17:30:36 +0200 Subject: [PATCH 3/5] Add ext .ows to filename automatically during saving new file. - reverts commit 4f02bf8420ff2ee68c607006a79bcd0a44135740. - Add question MessageBox when overriding file - changed place when `filename` is changed to `filename`.ows in compare to reverted commit. --- orangecanvas/application/canvasmain.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/orangecanvas/application/canvasmain.py b/orangecanvas/application/canvasmain.py index 19ff5d17..81f0cc9c 100644 --- a/orangecanvas/application/canvasmain.py +++ b/orangecanvas/application/canvasmain.py @@ -1557,8 +1557,15 @@ def save_scheme_as(self): # dialog.exec waits for user action if dialog.exec(): filename = dialog.selectedFiles()[0] + # Enforcing ".ows" extension during saving file. + filename = filename if str(filename).endswith('.ows') else f"{filename}.ows" + do_override = QMessageBox.question( + self, "Overwrite file?", + f"File {os.path.split(filename)[1]} already exists." + "\nOverwrite?" + ) == QMessageBox.Yes if os.path.exists(filename) else True settings.setValue("last-scheme-dir", os.path.dirname(filename)) - if self.save_scheme_to(curr_scheme, filename): + if do_override and self.save_scheme_to(curr_scheme, filename): document.setPath(filename) document.setModified(False) self.add_recent_scheme(curr_scheme.title, document.path()) @@ -1574,9 +1581,6 @@ def save_scheme_to(self, scheme, filename): `True`, else show a message to the user explaining the error and return `False`. """ - # Enforcing ".ows" extension during saving file. - # see .../biolab/orange-canvas-core/pull/330 - filename = filename if str(filename).endswith('.ows') else f"{filename}.ows" dirname, basename = os.path.split(filename) title = scheme.title or "untitled" From 62358506c74cd917008107a715705847c93e332c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20K=C5=82apyta?= <33362868+krystofair@users.noreply.github.com> Date: Sun, 7 Sep 2025 07:52:00 +0200 Subject: [PATCH 4/5] Fix for TestMainWindowLoad::test_save. - Add suffix to filename for file created by tempfile to be compatible with enforcing 'ows' extension. - Mocked `exec` method for QFileDialog now return True, because it was never reach out branch `if dialog.exec():`. --- orangecanvas/application/tests/test_mainwindow.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/orangecanvas/application/tests/test_mainwindow.py b/orangecanvas/application/tests/test_mainwindow.py index 927257c4..ff06a095 100644 --- a/orangecanvas/application/tests/test_mainwindow.py +++ b/orangecanvas/application/tests/test_mainwindow.py @@ -1,7 +1,7 @@ import io import os import tempfile -from unittest.mock import patch +from unittest.mock import patch, Mock from AnyQt.QtGui import QWhatsThisClickedEvent from AnyQt.QtWidgets import ( @@ -149,7 +149,7 @@ class TestMainWindowLoad(TestMainWindowBase): def setUp(self): super().setUp() - fd, filename = tempfile.mkstemp() + fd, filename = tempfile.mkstemp(suffix=".ows") self.file = os.fdopen(fd, "w+b") self.filename = filename @@ -182,8 +182,11 @@ def exec(myself): myself.setOption(QFileDialog.DontConfirmOverwrite) myself.selectFile(self.filename) myself.accept() + return True - with patch("AnyQt.QtWidgets.QFileDialog.exec", exec): + with (patch("AnyQt.QtWidgets.QFileDialog.exec", exec), + patch("AnyQt.QtWidgets.QMessageBox.question", + new=Mock(return_value=QMessageBox.Yes))): w.save_scheme() self.assertTrue(os.path.samefile(w.current_document().path(), self.filename)) From abe7ba9d586df4ff7f029e39df703ec9145de566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20K=C5=82apyta?= <33362868+krystofair@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:40:57 +0200 Subject: [PATCH 5/5] Extension enforcing in "good" way I haven't seen it when fixing tests... defaultSuffix Previously solution is bad, because there will pop-up two MessageBox, second when FileDialog is already closed. --- orangecanvas/application/canvasmain.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/orangecanvas/application/canvasmain.py b/orangecanvas/application/canvasmain.py index 81f0cc9c..13c2bd4c 100644 --- a/orangecanvas/application/canvasmain.py +++ b/orangecanvas/application/canvasmain.py @@ -1549,6 +1549,7 @@ def save_scheme_as(self): acceptMode=QFileDialog.AcceptSave, windowModality=Qt.WindowModal, objectName="save-as-ows-filedialog", + defaultSuffix=".ows" ) dialog.setNameFilter(self.tr("Orange Workflow (*.ows)")) # `deleteLater` can be ivoked before `exec` as PyQt 6.9 doc says, that @@ -1557,15 +1558,8 @@ def save_scheme_as(self): # dialog.exec waits for user action if dialog.exec(): filename = dialog.selectedFiles()[0] - # Enforcing ".ows" extension during saving file. - filename = filename if str(filename).endswith('.ows') else f"{filename}.ows" - do_override = QMessageBox.question( - self, "Overwrite file?", - f"File {os.path.split(filename)[1]} already exists." - "\nOverwrite?" - ) == QMessageBox.Yes if os.path.exists(filename) else True settings.setValue("last-scheme-dir", os.path.dirname(filename)) - if do_override and self.save_scheme_to(curr_scheme, filename): + if self.save_scheme_to(curr_scheme, filename): document.setPath(filename) document.setModified(False) self.add_recent_scheme(curr_scheme.title, document.path())