From 21517c502589000777d42e0b1c3dcd487e9260bc Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 5 Feb 2024 13:22:26 +0100 Subject: [PATCH 01/12] Adding openURL in singleinstance --- cura/SingleInstance.py | 6 +++++- packaging/NSIS/Ultimaker-Cura.nsi.jinja | 2 +- packaging/msi/UltiMaker-Cura.wxs.jinja | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 61ab1204fe1..2c3e816767a 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -92,7 +92,11 @@ def __readCommands(self, connection: QLocalSocket) -> None: # Command: Load a model or project file elif command == "open": - self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) + if payload["filePath"].file(): + self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) + if payload["filePath"].url(): + self._application.callLater(lambda f= payload["filepath"]: self._application._openUrl(f)) + # Command: Activate the window and bring it to the top. elif command == "focus": diff --git a/packaging/NSIS/Ultimaker-Cura.nsi.jinja b/packaging/NSIS/Ultimaker-Cura.nsi.jinja index 0a2ce0f517a..2106c473120 100644 --- a/packaging/NSIS/Ultimaker-Cura.nsi.jinja +++ b/packaging/NSIS/Ultimaker-Cura.nsi.jinja @@ -156,7 +156,7 @@ WriteRegStr HKCR "slicer" "" "URL:slicer" WriteRegStr HKCR "slicer" "URL Protocol" "" WriteRegStr HKCR "slicer\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE},1" WriteRegStr HKCR "slicer\shell" "" "open" -WriteRegStr HKCR "slicer\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" "%1"' +WriteRegStr HKCR "slicer\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" --single-instance "%1"' SectionEnd ###################################################################### diff --git a/packaging/msi/UltiMaker-Cura.wxs.jinja b/packaging/msi/UltiMaker-Cura.wxs.jinja index 21f017c8139..13c5d2842e7 100644 --- a/packaging/msi/UltiMaker-Cura.wxs.jinja +++ b/packaging/msi/UltiMaker-Cura.wxs.jinja @@ -165,7 +165,7 @@ - + From e554eb264e4e3f44752addcd9079c0257fdb24c3 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 5 Feb 2024 15:44:46 +0100 Subject: [PATCH 02/12] adding logging for debugging CURA-11596 --- cura/SingleInstance.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 2c3e816767a..ef04ef5ddc7 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -52,6 +52,7 @@ def startClient(self) -> bool: single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) for filename in self._files_to_open: + Logger.log("i",f"Filename isxxx {os.path(filename)}") payload = {"command": "open", "filePath": os.path.abspath(filename)} single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) @@ -73,14 +74,18 @@ def startServer(self) -> None: def _onClientConnected(self) -> None: Logger.log("i", "New connection received on our single-instance server") connection = None #type: Optional[QLocalSocket] + Logger.log("i","getting connection") if self._single_instance_server: connection = self._single_instance_server.nextPendingConnection() + Logger.log("i", f"here: {connection}") if connection is not None: + Logger.log("i","here2") connection.readyRead.connect(lambda c = connection: self.__readCommands(c)) def __readCommands(self, connection: QLocalSocket) -> None: line = connection.readLine() + Logger.log("i", f"line read is {line}") while len(line) != 0: # There is also a .canReadLine() try: payload = json.loads(str(line, encoding = "ascii").strip()) From 9c57627a8828d45b2336396b29a572eebc9d5a44 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 6 Feb 2024 12:17:38 +0100 Subject: [PATCH 03/12] Adding command for opening url CURA-11596 --- cura/SingleInstance.py | 3 --- packaging/msi/UltiMaker-Cura.wxs.jinja | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index ef04ef5ddc7..fb85e258dbf 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -74,13 +74,10 @@ def startServer(self) -> None: def _onClientConnected(self) -> None: Logger.log("i", "New connection received on our single-instance server") connection = None #type: Optional[QLocalSocket] - Logger.log("i","getting connection") if self._single_instance_server: connection = self._single_instance_server.nextPendingConnection() - Logger.log("i", f"here: {connection}") if connection is not None: - Logger.log("i","here2") connection.readyRead.connect(lambda c = connection: self.__readCommands(c)) def __readCommands(self, connection: QLocalSocket) -> None: diff --git a/packaging/msi/UltiMaker-Cura.wxs.jinja b/packaging/msi/UltiMaker-Cura.wxs.jinja index 13c5d2842e7..1f22dcc8bc9 100644 --- a/packaging/msi/UltiMaker-Cura.wxs.jinja +++ b/packaging/msi/UltiMaker-Cura.wxs.jinja @@ -166,6 +166,8 @@ + + From 8073bc0e50473ada7828d707674c9d3822f59b70 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 6 Feb 2024 16:04:08 +0100 Subject: [PATCH 04/12] adding this preference in mac-os doesn't work. It always opens it up in singlenstance CURA-11596 --- resources/qml/Preferences/GeneralPage.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 88719445233..0a430eda5da 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -627,6 +627,7 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width + visible: Qt.platform.os !== "osx" height: childrenRect.height text: catalog.i18nc("@info:tooltip","Should opening files from the desktop or external applications open in the same instance of Cura?") From eff45842038373ab436ca67e9fc2173458a09eb5 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 6 Feb 2024 16:05:34 +0100 Subject: [PATCH 05/12] adding files in single instance in case of url CURA-11596 --- cura/CuraApplication.py | 2 +- cura/SingleInstance.py | 33 ++++++++++++++----------- packaging/NSIS/Ultimaker-Cura.nsi.jinja | 2 +- packaging/msi/UltiMaker-Cura.wxs.jinja | 4 +-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 67c420028ba..bae609c07bc 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -356,7 +356,7 @@ def initialize(self) -> None: self._machine_action_manager.initialize() def __sendCommandToSingleInstance(self): - self._single_instance = SingleInstance(self, self._files_to_open) + self._single_instance = SingleInstance(self, self._files_to_open, self._open_url_queue) # If we use single instance, try to connect to the single instance server, send commands, and then exit. # If we cannot find an existing single instance server, this is the only instance, so just keep going. diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index fb85e258dbf..02fa6622aa7 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -7,14 +7,15 @@ from PyQt6.QtNetwork import QLocalServer, QLocalSocket -from UM.Qt.QtApplication import QtApplication #For typing. +from UM.Qt.QtApplication import QtApplication # For typing. from UM.Logger import Logger class SingleInstance: - def __init__(self, application: QtApplication, files_to_open: Optional[List[str]]) -> None: + def __init__(self, application: QtApplication, files_to_open: Optional[List[str]], url_to_open: Optional[List[str]]) -> None: self._application = application self._files_to_open = files_to_open + self._url_to_open = url_to_open self._single_instance_server = None @@ -33,7 +34,7 @@ def startClient(self) -> bool: return False # We only send the files that need to be opened. - if not self._files_to_open: + if not self._files_to_open or not self._url_to_open: Logger.log("i", "No file need to be opened, do nothing.") return True @@ -46,18 +47,21 @@ def startClient(self) -> bool: if self._application.getPreferences().getValue("cura/single_instance_clear_before_load"): payload = {"command": "clear-all"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) payload = {"command": "focus"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) for filename in self._files_to_open: - Logger.log("i",f"Filename isxxx {os.path(filename)}") payload = {"command": "open", "filePath": os.path.abspath(filename)} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) + + for filename in self._url_to_open: + payload = {"command": "open", "urlPath": os.path.abspath(filename)} + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) payload = {"command": "close-connection"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) single_instance_socket.flush() single_instance_socket.waitForDisconnected() @@ -73,7 +77,7 @@ def startServer(self) -> None: def _onClientConnected(self) -> None: Logger.log("i", "New connection received on our single-instance server") - connection = None #type: Optional[QLocalSocket] + connection = None # type: Optional[QLocalSocket] if self._single_instance_server: connection = self._single_instance_server.nextPendingConnection() @@ -82,10 +86,9 @@ def _onClientConnected(self) -> None: def __readCommands(self, connection: QLocalSocket) -> None: line = connection.readLine() - Logger.log("i", f"line read is {line}") - while len(line) != 0: # There is also a .canReadLine() + while len(line) != 0: # There is also a .canReadLine() try: - payload = json.loads(str(line, encoding = "ascii").strip()) + payload = json.loads(str(line, encoding="ascii").strip()) command = payload["command"] # Command: Remove all models from the build plate. @@ -96,8 +99,8 @@ def __readCommands(self, connection: QLocalSocket) -> None: elif command == "open": if payload["filePath"].file(): self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) - if payload["filePath"].url(): - self._application.callLater(lambda f= payload["filepath"]: self._application._openUrl(f)) + if payload["urlPath"].url(): + self._application.callLater(lambda f = payload["urlPath"]: self._application._openUrl(f)) # Command: Activate the window and bring it to the top. @@ -106,7 +109,7 @@ def __readCommands(self, connection: QLocalSocket) -> None: # 'alert' or flashing the icon in the taskbar is the best thing we do now. main_window = self._application.getMainWindow() if main_window is not None: - self._application.callLater(lambda: main_window.alert(0)) # type: ignore # I don't know why MyPy complains here + self._application.callLater(lambda: main_window.alert(0)) # type: ignore # I don't know why MyPy complains here # Command: Close the socket connection. We're done. elif command == "close-connection": diff --git a/packaging/NSIS/Ultimaker-Cura.nsi.jinja b/packaging/NSIS/Ultimaker-Cura.nsi.jinja index 2106c473120..0a2ce0f517a 100644 --- a/packaging/NSIS/Ultimaker-Cura.nsi.jinja +++ b/packaging/NSIS/Ultimaker-Cura.nsi.jinja @@ -156,7 +156,7 @@ WriteRegStr HKCR "slicer" "" "URL:slicer" WriteRegStr HKCR "slicer" "URL Protocol" "" WriteRegStr HKCR "slicer\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE},1" WriteRegStr HKCR "slicer\shell" "" "open" -WriteRegStr HKCR "slicer\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" --single-instance "%1"' +WriteRegStr HKCR "slicer\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" "%1"' SectionEnd ###################################################################### diff --git a/packaging/msi/UltiMaker-Cura.wxs.jinja b/packaging/msi/UltiMaker-Cura.wxs.jinja index 1f22dcc8bc9..21f017c8139 100644 --- a/packaging/msi/UltiMaker-Cura.wxs.jinja +++ b/packaging/msi/UltiMaker-Cura.wxs.jinja @@ -165,9 +165,7 @@ - - - + From 664fa4f48d215d9cf52e5f2437bd0ac21a1f9ba5 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 6 Feb 2024 16:19:01 +0100 Subject: [PATCH 06/12] assigning different command for opening url CURA-11596 --- cura/SingleInstance.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 02fa6622aa7..3357d22d303 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -56,8 +56,8 @@ def startClient(self) -> bool: payload = {"command": "open", "filePath": os.path.abspath(filename)} single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) - for filename in self._url_to_open: - payload = {"command": "open", "urlPath": os.path.abspath(filename)} + for url in self._url_to_open: + payload = {"command": "open-url", "urlPath": url} single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) payload = {"command": "close-connection"} @@ -88,7 +88,7 @@ def __readCommands(self, connection: QLocalSocket) -> None: line = connection.readLine() while len(line) != 0: # There is also a .canReadLine() try: - payload = json.loads(str(line, encoding="ascii").strip()) + payload = json.loads(str(line, encoding = "ascii").strip()) command = payload["command"] # Command: Remove all models from the build plate. @@ -97,10 +97,11 @@ def __readCommands(self, connection: QLocalSocket) -> None: # Command: Load a model or project file elif command == "open": - if payload["filePath"].file(): - self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) - if payload["urlPath"].url(): - self._application.callLater(lambda f = payload["urlPath"]: self._application._openUrl(f)) + self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) + + #command: Load a url link in Cura + elif command == "open-url": + self._application.callLater(lambda f = payload["urlPath"]: self._application._openUrl(f)) # Command: Activate the window and bring it to the top. From bb28a7c5063666b138be862acd607e0fa47a8d42 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 7 Feb 2024 11:00:54 +0100 Subject: [PATCH 07/12] fixing the if condition CURA-11596 --- cura/SingleInstance.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 3357d22d303..c4590fc2756 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -5,6 +5,7 @@ import os from typing import List, Optional +from PyQt6.QtCore import QUrl from PyQt6.QtNetwork import QLocalServer, QLocalSocket from UM.Qt.QtApplication import QtApplication # For typing. @@ -34,7 +35,7 @@ def startClient(self) -> bool: return False # We only send the files that need to be opened. - if not self._files_to_open or not self._url_to_open: + if not self._files_to_open and not self._url_to_open: Logger.log("i", "No file need to be opened, do nothing.") return True @@ -47,17 +48,17 @@ def startClient(self) -> bool: if self._application.getPreferences().getValue("cura/single_instance_clear_before_load"): payload = {"command": "clear-all"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) payload = {"command": "focus"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) for filename in self._files_to_open: payload = {"command": "open", "filePath": os.path.abspath(filename)} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) for url in self._url_to_open: - payload = {"command": "open-url", "urlPath": url} + payload = {"command": "open-url", "urlPath": url.toString()} single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii")) payload = {"command": "close-connection"} @@ -82,10 +83,12 @@ def _onClientConnected(self) -> None: connection = self._single_instance_server.nextPendingConnection() if connection is not None: + x = self.__readCommands(connection) connection.readyRead.connect(lambda c = connection: self.__readCommands(c)) def __readCommands(self, connection: QLocalSocket) -> None: line = connection.readLine() + print(f"line is {line}") while len(line) != 0: # There is also a .canReadLine() try: payload = json.loads(str(line, encoding = "ascii").strip()) @@ -101,7 +104,8 @@ def __readCommands(self, connection: QLocalSocket) -> None: #command: Load a url link in Cura elif command == "open-url": - self._application.callLater(lambda f = payload["urlPath"]: self._application._openUrl(f)) + url = QUrl(payload["urlPath"]) + self._application.callLater(lambda f = url: self._application._openUrl(f)) # Command: Activate the window and bring it to the top. From 206b53072465273db18be15ca398ae116157dbb1 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 7 Feb 2024 11:05:48 +0100 Subject: [PATCH 08/12] Removing debug statement CURA-11596 --- cura/SingleInstance.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index c4590fc2756..c0810442014 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -88,7 +88,6 @@ def _onClientConnected(self) -> None: def __readCommands(self, connection: QLocalSocket) -> None: line = connection.readLine() - print(f"line is {line}") while len(line) != 0: # There is also a .canReadLine() try: payload = json.loads(str(line, encoding = "ascii").strip()) From 313a7f7068ef494e4e1b6f4fd84669434340302a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 7 Feb 2024 12:16:01 +0100 Subject: [PATCH 09/12] remove debug statement CURA-11596 --- cura/SingleInstance.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index c0810442014..8c4018a4f7b 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -83,7 +83,6 @@ def _onClientConnected(self) -> None: connection = self._single_instance_server.nextPendingConnection() if connection is not None: - x = self.__readCommands(connection) connection.readyRead.connect(lambda c = connection: self.__readCommands(c)) def __readCommands(self, connection: QLocalSocket) -> None: From cf1a7889c237a25e92f6307572e750d4dc3ae822 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 7 Feb 2024 17:01:56 +0100 Subject: [PATCH 10/12] mimicing behavior as files to open CURA-11596 --- cura/CuraApplication.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 8a87ba448cb..51ceecd7dd6 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -180,6 +180,7 @@ def __init__(self, *args, **kwargs): # Variables set from CLI self._files_to_open = [] + self._urls_to_open = [] self._use_single_instance = False self._single_instance = None @@ -334,7 +335,7 @@ def parseCliOptions(self): for filename in self._cli_args.file: url = QUrl(filename) if url.scheme() in self._supported_url_schemes: - self._open_url_queue.append(url) + self._urls_to_open.append(url) else: self._files_to_open.append(os.path.abspath(filename)) @@ -357,7 +358,7 @@ def initialize(self) -> None: self._machine_action_manager.initialize() def __sendCommandToSingleInstance(self): - self._single_instance = SingleInstance(self, self._files_to_open, self._open_url_queue) + self._single_instance = SingleInstance(self, self._files_to_open, self._urls_to_open) # If we use single instance, try to connect to the single instance server, send commands, and then exit. # If we cannot find an existing single instance server, this is the only instance, so just keep going. @@ -963,6 +964,8 @@ def _onPostStart(self): self.callLater(self._openFile, file_name) for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading. self.callLater(self._openFile, file_name) + for url in self._urls_to_open: + self.callLater(self._openUrl, url) for url in self._open_url_queue: self.callLater(self._openUrl, url) From 495be39d1f7b4c62419edd5742a497f123c2f741 Mon Sep 17 00:00:00 2001 From: Saumya Jain <70144862+saumyaj3@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:51:49 +0100 Subject: [PATCH 11/12] Update cura/SingleInstance.py Co-authored-by: Casper Lamboo --- cura/SingleInstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 8c4018a4f7b..0448ddc6478 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -103,7 +103,7 @@ def __readCommands(self, connection: QLocalSocket) -> None: #command: Load a url link in Cura elif command == "open-url": url = QUrl(payload["urlPath"]) - self._application.callLater(lambda f = url: self._application._openUrl(f)) + self._application.callLater(lambda: self._application._openUrl(url)) # Command: Activate the window and bring it to the top. From a251a5d2ca1cdba0a1d8971968b49f5076720c95 Mon Sep 17 00:00:00 2001 From: Saumya Jain <70144862+saumyaj3@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:51:56 +0100 Subject: [PATCH 12/12] Update resources/qml/Preferences/GeneralPage.qml Co-authored-by: Casper Lamboo --- resources/qml/Preferences/GeneralPage.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 0a430eda5da..c313ffbd948 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -627,6 +627,7 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width + // Mac only allows applications to run as a single instance, so providing the option for this os doesn't make much sense visible: Qt.platform.os !== "osx" height: childrenRect.height text: catalog.i18nc("@info:tooltip","Should opening files from the desktop or external applications open in the same instance of Cura?")