From 04d8d252c2027af489070705b7a889cd5d488433 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Tue, 26 Apr 2022 20:45:05 +0200 Subject: [PATCH] retain compatibility with older Cura versions because the Ultimaker Marketplace only supports one plugin version globally for all Cura's --- CHANGELOG.md | 3 + DuetRRFAction.py | 11 ++- DuetRRFOutputDevice.py | 18 +++- DuetRRFPlugin.py | 5 +- plugin.json | 4 +- resources/qml/DuetRRFAction.qml | 1 - resources/qml/UploadFilename.qml | 1 - resources/qml/legacy/DuetRRFAction.qml | 113 ++++++++++++++++++++++++ resources/qml/legacy/UploadFilename.qml | 65 ++++++++++++++ thumbnails.py | 11 ++- 10 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 resources/qml/legacy/DuetRRFAction.qml create mode 100644 resources/qml/legacy/UploadFilename.qml diff --git a/CHANGELOG.md b/CHANGELOG.md index f7bb7da..be61ba2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog of Cura-DuetRRFPlugin +## v1.2.7: 2022-04-26 +* bump compatibility for Cura 5.0 / API 8.0, while retaining compatibility with Cura 4.11 / 7.7 and up + ## v1.2.6: 2022-04-23 * bump compatibility for Cura 5.0 / API 8.0, for older Cura versions, please use plugin v1.2.5 or older * fixed simulation progress reports diff --git a/DuetRRFAction.py b/DuetRRFAction.py index 27fd987..a712bc5 100644 --- a/DuetRRFAction.py +++ b/DuetRRFAction.py @@ -1,8 +1,12 @@ import os import re +import sys from typing import Optional -from PyQt6.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +try: # Cura 5 + from PyQt6.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +except: # Cura 4 + from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from cura.CuraApplication import CuraApplication from cura.MachineAction import MachineAction @@ -20,7 +24,10 @@ class DuetRRFAction(MachineAction): def __init__(self, parent: QObject = None) -> None: super().__init__("DuetRRFAction", catalog.i18nc("@action", "Connect Duet RepRapFirmware")) - self._qml_url = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', 'qml', 'DuetRRFAction.qml') + extra_path = "" + if "PyQt5" in sys.modules: # Cura 4 + extra_path = "legacy" + self._qml_url = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', 'qml', extra_path, 'DuetRRFAction.qml') self._application = CuraApplication.getInstance() self._application.globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) diff --git a/DuetRRFOutputDevice.py b/DuetRRFOutputDevice.py index 46477e9..c4ba7f3 100644 --- a/DuetRRFOutputDevice.py +++ b/DuetRRFOutputDevice.py @@ -1,3 +1,4 @@ +import sys import os.path import datetime import urllib @@ -7,9 +8,14 @@ from typing import cast from enum import Enum -from PyQt6.QtNetwork import QNetworkReply -from PyQt6.QtCore import QUrl, QObject, QByteArray, QTimer -from PyQt6.QtGui import QDesktopServices +try: # Cura 5 + from PyQt6.QtNetwork import QNetworkReply + from PyQt6.QtCore import QUrl, QObject, QByteArray, QTimer + from PyQt6.QtGui import QDesktopServices +except: # Cura 4 + from PyQt5.QtNetwork import QNetworkReply + from PyQt5.QtCore import QUrl, QObject, QByteArray, QTimer + from PyQt5.QtGui import QDesktopServices from cura.CuraApplication import CuraApplication @@ -171,7 +177,11 @@ def requestWrite(self, node, fileName=None, *args, **kwargs): fileName = "%s.gcode" % Application.getInstance().getPrintInformation().jobName self._fileName = fileName - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', 'qml', 'UploadFilename.qml') + extra_path = "" + if "PyQt5" in sys.modules: # Cura 4 + extra_path = "legacy" + path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', 'qml', extra_path, 'UploadFilename.qml') + self._dialog = CuraApplication.getInstance().createQmlComponent(path, {"manager": self}) self._dialog.textChanged.connect(self._onFilenameChanged) self._dialog.accepted.connect(self._onFilenameAccepted) diff --git a/DuetRRFPlugin.py b/DuetRRFPlugin.py index 60cf20b..71cd065 100644 --- a/DuetRRFPlugin.py +++ b/DuetRRFPlugin.py @@ -1,6 +1,9 @@ import json -from PyQt6.QtCore import QTimer +try: # Cura 5 + from PyQt6.QtCore import QTimer +except: # Cura 4 + from PyQt5.QtCore import QTimer from cura.CuraApplication import CuraApplication from cura.Settings.CuraContainerRegistry import CuraContainerRegistry diff --git a/plugin.json b/plugin.json index 97e2d1c..8f32ab2 100644 --- a/plugin.json +++ b/plugin.json @@ -2,6 +2,6 @@ "name": "DuetRRF", "author": "Thomas Kriechbaumer", "description": "Upload and Print to Duet 2 Wifi / Duet 2 Ethernet / Duet 2 Maestro / Duet 3 with RepRapFirmware.", - "version": "1.2.6", - "supported_sdk_versions": ["8.0.0"] + "version": "1.2.7", + "supported_sdk_versions": ["7.7.0", "7.8.0", "7.9.0", "8.0.0"] } diff --git a/resources/qml/DuetRRFAction.qml b/resources/qml/DuetRRFAction.qml index d5562e6..e04c42f 100644 --- a/resources/qml/DuetRRFAction.qml +++ b/resources/qml/DuetRRFAction.qml @@ -6,7 +6,6 @@ import QtQuick.Window 2.2 import UM 1.5 as UM import Cura 1.1 as Cura - Cura.MachineAction { id: base; diff --git a/resources/qml/UploadFilename.qml b/resources/qml/UploadFilename.qml index 47634e6..66c822d 100644 --- a/resources/qml/UploadFilename.qml +++ b/resources/qml/UploadFilename.qml @@ -1,7 +1,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 -import QtQuick.Dialogs import UM 1.5 as UM import Cura 1.1 as Cura diff --git a/resources/qml/legacy/DuetRRFAction.qml b/resources/qml/legacy/DuetRRFAction.qml new file mode 100644 index 0000000..80d4d12 --- /dev/null +++ b/resources/qml/legacy/DuetRRFAction.qml @@ -0,0 +1,113 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Cura.MachineAction +{ + id: base; + + property var finished: manager.finished + onFinishedChanged: if(manager.finished) {completed()} + + function reset() + { + manager.reset() + } + + anchors.fill: parent; + property var selectedInstance: null + + property bool validUrl: true; + + Component.onCompleted: { + actionDialog.minimumWidth = screenScaleFactor * 500; + actionDialog.minimumHeight = screenScaleFactor * 255; + actionDialog.maximumWidth = screenScaleFactor * 500; + actionDialog.maximumHeight = screenScaleFactor * 255; + } + + Column { + anchors.fill: parent; + + Item { width: parent.width; } + Label { text: catalog.i18nc("@label", "Duet Address (URL)"); } + TextField { + id: urlField; + text: manager.printerSettingUrl; + maximumLength: 1024; + anchors.left: parent.left; + anchors.right: parent.right; + onTextChanged: { + base.validUrl = manager.validUrl(urlField.text); + } + } + + Item { width: parent.width; } + Label { text: catalog.i18nc("@label", "Duet Password (if you used M551)"); } + TextField { + id: duet_passwordField; + text: manager.printerSettingDuetPassword; + maximumLength: 1024; + anchors.left: parent.left; + anchors.right: parent.right; + } + + Item { width: parent.width; } + Label { text: catalog.i18nc("@label", "HTTP Basic Auth: user (if you run a reverse proxy)"); } + TextField { + id: http_userField; + text: manager.printerSettingHTTPUser; + maximumLength: 1024; + anchors.left: parent.left; + anchors.right: parent.right; + } + + Item { width: parent.width; } + Label { text: catalog.i18nc("@label", "HTTP Basic Auth: password (if you run a reverse proxy)"); } + TextField { + id: http_passwordField; + text: manager.printerSettingHTTPPassword; + maximumLength: 1024; + anchors.left: parent.left; + anchors.right: parent.right; + } + + Item { width: parent.width; } + Label { + visible: !base.validUrl; + text: catalog.i18nc("@error", "URL not valid. Example: http://192.168.1.42/"); + color: "red"; + } + + Item { + width: saveButton.implicitWidth + height: saveButton.implicitHeight + } + + Button { + id: saveButton; + text: catalog.i18nc("@action:button", "Save Config"); + width: screenScaleFactor * 100; + onClicked: { + manager.saveConfig(urlField.text, duet_passwordField.text, http_userField.text, http_passwordField.text); + actionDialog.reject(); + } + enabled: base.validUrl; + } + + Button { + id: deleteButton; + text: catalog.i18nc("@action:button", "Delete Config"); + width: screenScaleFactor * 100; + onClicked: { + manager.deleteConfig(); + actionDialog.reject(); + } + } + } +} diff --git a/resources/qml/legacy/UploadFilename.qml b/resources/qml/legacy/UploadFilename.qml new file mode 100644 index 0000000..74e9005 --- /dev/null +++ b/resources/qml/legacy/UploadFilename.qml @@ -0,0 +1,65 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 +import QtQuick.Window 2.1 + +import UM 1.1 as UM + +UM.Dialog +{ + id: base; + property string object: ""; + + property alias newName: nameField.text; + property bool validName: true; + property string validationError; + property string dialogTitle: "Upload Filename"; + + title: dialogTitle; + + minimumWidth: screenScaleFactor * 400 + minimumHeight: screenScaleFactor * 120 + + property variant catalog: UM.I18nCatalog { name: "uranium"; } + + signal textChanged(string text); + signal selectText() + onSelectText: { + nameField.selectAll(); + nameField.focus = true; + } + + Column { + anchors.fill: parent; + + TextField { + objectName: "nameField"; + id: nameField; + width: parent.width; + text: base.object; + maximumLength: 100; + onTextChanged: base.textChanged(text); + Keys.onReturnPressed: { if (base.validName) base.accept(); } + Keys.onEnterPressed: { if (base.validName) base.accept(); } + Keys.onEscapePressed: base.reject(); + } + + Label { + visible: !base.validName; + text: base.validationError; + } + } + + rightButtons: [ + Button { + text: catalog.i18nc("@action:button", "Cancel"); + onClicked: base.reject(); + }, + Button { + text: catalog.i18nc("@action:button", "OK"); + onClicked: base.accept(); + enabled: base.validName; + isDefault: true; + } + ] +} diff --git a/thumbnails.py b/thumbnails.py index 1d9522b..b4640dd 100644 --- a/thumbnails.py +++ b/thumbnails.py @@ -2,9 +2,14 @@ import traceback from io import StringIO -from PyQt6 import QtCore -from PyQt6.QtCore import QCoreApplication, QBuffer -from PyQt6.QtGui import QImage +try: # Cura 5 + from PyQt6 import QtCore + from PyQt6.QtCore import QCoreApplication, QBuffer + from PyQt6.QtGui import QImage +except: # Cura 4 + from PyQt5 import QtCore + from PyQt5.QtCore import QCoreApplication, QBuffer + from PyQt5.QtGui import QImage from UM.Application import Application from UM.Logger import Logger