Merge branch 'master' into libArachne_rebased

This commit is contained in:
Jelle Spijker 2021-08-12 18:50:29 +02:00
commit 4986861727
No known key found for this signature in database
GPG key ID: 6662DC033BE6B99A
384 changed files with 16324 additions and 8810 deletions

View file

@ -159,7 +159,8 @@ class CuraEngineBackend(QObject, Backend):
self._slicing_error_message = Message(
text = catalog.i18nc("@message", "Slicing failed with an unexpected error. Please consider reporting a bug on our issue tracker."),
title = catalog.i18nc("@message:title", "Slicing failed")
title = catalog.i18nc("@message:title", "Slicing failed"),
message_type = Message.MessageType.ERROR
)
self._slicing_error_message.addAction(
action_id = "report_bug",

View file

@ -49,11 +49,27 @@ Item
id: searchBar
Layout.fillWidth: true
implicitHeight: createNewProjectButton.height
leftPadding: searchIcon.width + UM.Theme.getSize("default_margin").width * 2
onTextEdited: manager.projectFilter = text //Update the search filter when editing this text field.
leftIcon: UM.Theme.getIcon("Magnifier")
placeholderText: "Search"
UM.RecolorImage
{
id: searchIcon
anchors
{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: UM.Theme.getSize("default_margin").width
}
source: UM.Theme.getIcon("search")
height: UM.Theme.getSize("small_button_icon").height
width: height
color: UM.Theme.getColor("text")
}
}
Cura.SecondaryButton
@ -76,12 +92,11 @@ Item
id: upgradePlanButton
text: "Upgrade plan"
iconSource: UM.Theme.getIcon("LinkExternal")
iconSource: UM.Theme.getIcon("external_link")
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
tooltip: "You have reached the maximum number of projects allowed by your subscription. Please upgrade to the Professional subscription to create more projects."
tooltipWidth: parent.width * 0.5
tooltip: "Maximum number of projects reached. Please upgrade your subscription to create more projects."
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-essentials/sign-up-cura?utm_source=cura&utm_medium=software&utm_campaign=lib-max")
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/enterprise-software?utm_source=cura&utm_medium=software&utm_campaign=MaxProjLink")
}
}
@ -124,7 +139,7 @@ Item
id: visitDigitalLibraryButton
anchors.horizontalCenter: parent.horizontalCenter
text: "Visit Digital Library"
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library?utm_source=cura&utm_medium=software&utm_campaign=empty-library")
visible: searchBar.text === "" //Show the link to Digital Library when there are no projects in the user's Library.
}
}

View file

@ -0,0 +1,15 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from cura.CuraApplication import CuraApplication
from UM.Message import Message
from UM.Version import Version
def getBackwardsCompatibleMessage(text: str, title: str, lifetime: int, message_type_str: str) -> Message:
if CuraApplication.getInstance().getAPIVersion() < Version("7.7.0"):
return Message(text=text, title=title, lifetime=lifetime)
else:
message_type = Message.MessageType.NEUTRAL
if ("MessageType." + message_type_str) in [str(item) for item in Message.MessageType]:
message_type = Message.MessageType[message_type_str]
return Message(text=text, title=title, lifetime=lifetime, message_type=message_type)

View file

@ -14,6 +14,7 @@ from UM.Logger import Logger
from UM.Message import Message
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from .BackwardsCompatibleMessage import getBackwardsCompatibleMessage
from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
@ -69,11 +70,11 @@ class DFFileExportAndUploadManager:
use_inactivity_timer = False
)
self._generic_success_message = Message(
self._generic_success_message = getBackwardsCompatibleMessage(
text = "Your {} uploaded to '{}'.".format("file was" if len(self._file_upload_job_metadata) <= 1 else "files were", self._library_project_name),
title = "Upload successful",
lifetime = 0,
message_type = Message.MessageType.POSITIVE
message_type_str = "POSITIVE"
)
self._generic_success_message.addAction(
"open_df_project",
@ -217,11 +218,11 @@ class DFFileExportAndUploadManager:
# Set the progress to 100% when the upload job fails, to avoid having the progress message stuck
self._file_upload_job_metadata[filename]["upload_status"] = "failed"
self._file_upload_job_metadata[filename]["upload_progress"] = 100
self._file_upload_job_metadata[filename]["file_upload_failed_message"] = Message(
self._file_upload_job_metadata[filename]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
text = "Failed to export the file '{}'. The upload process is aborted.".format(filename),
title = "Export error",
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
self._on_upload_error()
self._onFileUploadFinished(filename)
@ -240,11 +241,11 @@ class DFFileExportAndUploadManager:
self._file_upload_job_metadata[filename_3mf]["upload_progress"] = 100
human_readable_error = self.extractErrorTitle(reply_string)
self._file_upload_job_metadata[filename_3mf]["file_upload_failed_message"] = Message(
self._file_upload_job_metadata[filename_3mf]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
text = "Failed to upload the file '{}' to '{}'. {}".format(filename_3mf, self._library_project_name, human_readable_error),
title = "File upload error",
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
self._on_upload_error()
self._onFileUploadFinished(filename_3mf)
@ -263,11 +264,11 @@ class DFFileExportAndUploadManager:
self._file_upload_job_metadata[filename_ufp]["upload_progress"] = 100
human_readable_error = self.extractErrorTitle(reply_string)
self._file_upload_job_metadata[filename_ufp]["file_upload_failed_message"] = Message(
self._file_upload_job_metadata[filename_ufp]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
title = "File upload error",
text = "Failed to upload the file '{}' to '{}'. {}".format(filename_ufp, self._library_project_name, human_readable_error),
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
self._on_upload_error()
self._onFileUploadFinished(filename_ufp)
@ -300,11 +301,11 @@ class DFFileExportAndUploadManager:
self._file_upload_job_metadata[filename]["upload_status"] = "failed"
self._file_upload_job_metadata[filename]["upload_progress"] = 100
human_readable_error = self.extractErrorTitle(reply_string)
self._file_upload_job_metadata[filename]["file_upload_failed_message"] = Message(
self._file_upload_job_metadata[filename]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
title = "File upload error",
text = "Failed to upload the file '{}' to '{}'. {}".format(self._file_name, self._library_project_name, human_readable_error),
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
self._on_upload_error()
@ -319,7 +320,7 @@ class DFFileExportAndUploadManager:
def _onMessageActionTriggered(self, message, action):
if action == "open_df_project":
project_url = "{}/app/library/project/{}?wait_for_new_files=true".format(CuraApplication.getInstance().ultimakerDigitalFactoryUrl, self._library_project_id)
project_url = "{}/app/library/project/{}?wait_for_new_files=true&utm_source=cura&utm_medium=software&utm_campaign=saved-library-file-message".format(CuraApplication.getInstance().ultimakerDigitalFactoryUrl, self._library_project_id)
QDesktopServices.openUrl(QUrl(project_url))
message.hide()
@ -337,17 +338,17 @@ class DFFileExportAndUploadManager:
"upload_progress" : -1,
"upload_status" : "",
"file_upload_response": None,
"file_upload_success_message": Message(
"file_upload_success_message": getBackwardsCompatibleMessage(
text = "'{}' was uploaded to '{}'.".format(filename_3mf, self._library_project_name),
title = "Upload successful",
lifetime = 0,
message_type = Message.MessageType.POSITIVE
message_type_str = "POSITIVE"
),
"file_upload_failed_message": Message(
"file_upload_failed_message": getBackwardsCompatibleMessage(
text = "Failed to upload the file '{}' to '{}'.".format(filename_3mf, self._library_project_name),
title = "File upload error",
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
}
job_3mf = ExportFileJob(self._file_handlers["3mf"], self._nodes, self._file_name, "3mf")
@ -361,17 +362,17 @@ class DFFileExportAndUploadManager:
"upload_progress" : -1,
"upload_status" : "",
"file_upload_response": None,
"file_upload_success_message": Message(
"file_upload_success_message": getBackwardsCompatibleMessage(
text = "'{}' was uploaded to '{}'.".format(filename_ufp, self._library_project_name),
title = "Upload successful",
lifetime = 0,
message_type = Message.MessageType.POSITIVE
message_type_str = "POSITIVE"
),
"file_upload_failed_message": Message(
"file_upload_failed_message": getBackwardsCompatibleMessage(
text = "Failed to upload the file '{}' to '{}'.".format(filename_ufp, self._library_project_name),
title = "File upload error",
lifetime = 0,
message_type = Message.MessageType.ERROR
message_type_str = "ERROR"
)
}
job_ufp = ExportFileJob(self._file_handlers["ufp"], self._nodes, self._file_name, "ufp")

View file

@ -23,6 +23,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from cura.API import Account
from cura.CuraApplication import CuraApplication
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from .BackwardsCompatibleMessage import getBackwardsCompatibleMessage
from .DFFileExportAndUploadManager import DFFileExportAndUploadManager
from .DigitalFactoryApiClient import DigitalFactoryApiClient
from .DigitalFactoryFileModel import DigitalFactoryFileModel
@ -527,11 +528,11 @@ class DigitalFactoryController(QObject):
except IOError as ex:
Logger.logException("e", "Can't write Digital Library file {0}/{1} download to temp-directory {2}.",
ex, project_name, file_name, temp_dir)
Message(
getBackwardsCompatibleMessage(
text = "Failed to write to temporary file for '{}'.".format(file_name),
title = "File-system error",
lifetime = 10,
message_type=Message.MessageType.ERROR
message_type_str="ERROR"
).show()
return
@ -542,11 +543,11 @@ class DigitalFactoryController(QObject):
f = file_name) -> None:
progress_message.hide()
Logger.error("An error {0} {1} occurred while downloading {2}/{3}".format(str(error), str(reply), p, f))
Message(
getBackwardsCompatibleMessage(
text = "Failed Digital Library download for '{}'.".format(f),
title = "Network error {}".format(error),
lifetime = 10,
message_type=Message.MessageType.ERROR
message_type_str="ERROR"
).show()
download_manager = HttpRequestManager.getInstance()
@ -591,10 +592,10 @@ class DigitalFactoryController(QObject):
if filename == "":
Logger.log("w", "The file name cannot be empty.")
Message(text = "Cannot upload file with an empty name to the Digital Library",
getBackwardsCompatibleMessage(text = "Cannot upload file with an empty name to the Digital Library",
title = "Empty file name provided",
lifetime = 0,
message_type = Message.MessageType.ERROR).show()
message_type_str = "ERROR").show()
return
self._saveFileToSelectedProjectHelper(filename, formats)

View file

@ -112,7 +112,7 @@ class FirmwareUpdateCheckerJob(Job):
# The first time we want to store the current version, the notification will not be shown,
# because the new version of Cura will be release before the firmware and we don't want to
# notify the user when no new firmware version is available.
if (checked_version != "") and (checked_version != current_version):
if checked_version != "" and checked_version != current_version:
Logger.log("i", "Showing firmware update message for new version: {version}".format(version = current_version))
message = FirmwareUpdateCheckerMessage(machine_id, self._machine_name, current_version,
self._lookups.getRedirectUserUrl())

View file

@ -14,12 +14,12 @@ class FirmwareUpdateCheckerMessage(Message):
def __init__(self, machine_id: int, machine_name: str, latest_version: str, download_url: str) -> None:
super().__init__(i18n_catalog.i18nc(
"@info Don't translate {machine_name}, since it gets replaced by a printer name!",
"New features or bug-fixes may be available for your {machine_name}! If not already at the latest version, "
"New features or bug-fixes may be available for your {machine_name}! If you haven't done so already, "
"it is recommended to update the firmware on your printer to version {latest_version}.").format(
machine_name = machine_name, latest_version = latest_version),
title = i18n_catalog.i18nc(
"@info:title The %s gets replaced with the printer name.",
"New %s firmware available") % machine_name)
"New %s stable firmware available") % machine_name)
self._machine_id = machine_id
self._download_url = download_url

View file

@ -39,7 +39,7 @@ Rectangle
text: catalog.i18nc("@info:tooltip", "Go to Web Marketplace")
Label
{
text: "<a href='%2'>".arg(toolbox.getWebMarketplaceUrl("materials")) + catalog.i18nc("@label", "Search materials") + "</a>"
text: "<a href='%2'>".arg(toolbox.getWebMarketplaceUrl("materials") + "?utm_source=cura&utm_medium=software&utm_campaign=marketplace-search") + catalog.i18nc("@label", "Search materials") + "</a>"
width: contentWidth
height: contentHeight
horizontalAlignment: Text.AlignRight

View file

@ -91,7 +91,7 @@ Item
verticalCenter: parent.verticalCenter
}
acceptedButtons: Qt.LeftButton
onClicked: Qt.openUrlExternally(toolbox.getWebMarketplaceUrl("plugins"))
onClicked: Qt.openUrlExternally(toolbox.getWebMarketplaceUrl("plugins") + "?utm_source=cura&utm_medium=software&utm_campaign=marketplace-button")
UM.RecolorImage
{
id: cloudMarketplaceButton

View file

@ -58,7 +58,6 @@ class PackagesModel(ListModel):
items = []
if self._metadata is None:
Logger.logException("w", "Failed to load packages for Marketplace")
self.setItems(items)
return

View file

@ -183,12 +183,15 @@ class Toolbox(QObject, Extension):
self._application.getCuraAPI().account.loginStateChanged.connect(self._restart)
preferences = CuraApplication.getInstance().getPreferences()
preferences.addPreference("info/automatic_plugin_update_check", True)
# On boot we check which packages have updates.
if CuraApplication.getInstance().getPreferences().getValue("info/automatic_update_check") and len(installed_package_ids_with_versions) > 0:
if preferences.getValue("info/automatic_plugin_update_check") and len(installed_package_ids_with_versions) > 0:
# Request the latest and greatest!
self._makeRequestByType("updates")
def _fetchPackageData(self) -> None:
self._makeRequestByType("packages")
self._makeRequestByType("authors")
@ -648,8 +651,11 @@ class Toolbox(QObject, Extension):
self.resetDownload()
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
Logger.log("w", "Failed to download package. The following error was returned: %s",
json.loads(reply.readAll().data().decode("utf-8")))
try:
reply_error = json.loads(reply.readAll().data().decode("utf-8"))
except Exception as e:
reply_error = str(e)
Logger.log("w", "Failed to download package. The following error was returned: %s", reply_error)
return
# Must not delete the temporary file on Windows
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)

View file

@ -30,7 +30,7 @@ Button
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
color: UM.Theme.getColor("primary")
color: enabled ? UM.Theme.getColor("primary") : UM.Theme.getColor("main_background")
height: width
source: iconSource
width: Math.round(parent.width / 2)

View file

@ -173,7 +173,7 @@ Cura.MachineAction
anchors.right: parent.right
wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "If your printer is not listed, read the <a href='%1'>network printing troubleshooting guide</a>").arg("https://support.ultimaker.com/hc/en-us/articles/360012795419");
text: catalog.i18nc("@label", "If your printer is not listed, read the <a href='%1'>network printing troubleshooting guide</a>").arg("https://ultimaker.com/en/cura/troubleshooting/network?utm_source=cura&utm_medium=software&utm_campaign=manage-network-printer");
onLinkActivated: Qt.openUrlExternally(link)
}

View file

@ -40,7 +40,6 @@ Item
width: 240 * screenScaleFactor // TODO: Theme!
color: UM.Theme.getColor("monitor_tooltip_text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
}
}

View file

@ -271,8 +271,8 @@ Item
}
// For cloud printing, add this mouse area over the disabled cameraButton to indicate that it's not available
//Warning message is commented out because it's factually incorrect. Fix CURA-7637 to allow camera connections via cloud.
/* MouseArea
// Fix CURA-7637 to allow camera connections via cloud.
MouseArea
{
id: cameraDisabledButtonArea
anchors.fill: cameraButton
@ -282,13 +282,13 @@ Item
enabled: !cameraButton.enabled
}
MonitorInfoBlurb
{
id: cameraDisabledInfo
text: catalog.i18nc("@info", "The webcam is not available because you are monitoring a cloud printer.")
text: catalog.i18nc("@info", "Webcam feeds for cloud printers cannot be viewed from Ultimaker Cura." +
" Click \"Manage printer\" to visit Ultimaker Digital Factory and view this webcam.")
target: cameraButton
}*/
}
}
// Divider

View file

@ -262,7 +262,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
icon="",
description=I18N_CATALOG.i18nc("@action:tooltip", "Track the print in Ultimaker Digital Factory"),
button_align=message.ActionButtonAlignment.ALIGN_RIGHT)
df_url = f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=monitor-button"
df_url = f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=message-printjob-sent"
message.pyQtActionTriggered.connect(lambda message, action: (QDesktopServices.openUrl(QUrl(df_url)), message.hide()))
message.show()
@ -334,11 +334,11 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
@pyqtSlot(name="openPrintJobControlPanel")
def openPrintJobControlPanel(self) -> None:
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl))
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl + "?utm_source=cura&utm_medium=software&utm_campaign=monitor-manage-browser"))
@pyqtSlot(name="openPrinterControlPanel")
def openPrinterControlPanel(self) -> None:
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl))
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl + "?utm_source=cura&utm_medium=software&utm_campaign=monitor-manage-printer"))
@property
def clusterData(self) -> CloudClusterResponse:
@ -357,4 +357,4 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
"""Gets the URL on which to monitor the cluster via the cloud."""
root_url_prefix = "-staging" if self._account.is_staging else ""
return "https://mycloud{}.ultimaker.com/app/jobs/{}".format(root_url_prefix, self.clusterData.cluster_id)
return "https://digitalfactory{}.ultimaker.com/app/jobs/{}".format(root_url_prefix, self.clusterData.cluster_id)

View file

@ -332,7 +332,7 @@ class CloudOutputDeviceManager:
message_text += self.i18n_catalog.i18nc(
"info:status",
"To establish a connection, please visit the {website_link}".format(website_link = "<a href='https://digitalfactory.ultimaker.com/'>{}</a>.".format(digital_factory_string))
"To establish a connection, please visit the {website_link}".format(website_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-connect-printer'>{}</a>.".format(digital_factory_string))
)
self._removed_printers_message.setText(message_text)
self._removed_printers_message.addAction("keep_printer_configurations_action",
@ -419,7 +419,7 @@ class CloudOutputDeviceManager:
machine.setMetaDataEntry("group_name", device.name)
machine.setMetaDataEntry("group_size", device.clusterSize)
digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory")
digital_factory_link = "<a href='https://digitalfactory.ultimaker.com/'>{digital_factory_string}</a>".format(digital_factory_string = digital_factory_string)
digital_factory_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-remove-printer'>{digital_factory_string}</a>".format(digital_factory_string = digital_factory_string)
removal_warning_string = self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "{printer_name} will be removed until the next account sync.").format(printer_name = device.name) \
+ "<br>" + self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "To remove {printer_name} permanently, visit {digital_factory_link}").format(printer_name = device.name, digital_factory_link = digital_factory_link) \
+ "<br><br>" + self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "Are you sure you want to remove {printer_name} temporarily?").format(printer_name = device.name)