Merge branch '5.0'

This commit is contained in:
Ghostkeeper 2022-04-21 14:17:56 +02:00
commit bb56a999ec
No known key found for this signature in database
GPG key ID: 68F39EA88EEED5FF
89 changed files with 1432 additions and 264 deletions

View file

@ -1,4 +1,4 @@
# Copyright (c) 2021 Ultimaker B.V.
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
# ---------
@ -13,7 +13,7 @@ DEFAULT_CURA_DEBUG_MODE = False
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
# CuraVersion.py.in template.
CuraSDKVersion = "7.9.0"
CuraSDKVersion = "8.0.0"
try:
from cura.CuraVersion import CuraAppName # type: ignore

View file

@ -53,6 +53,8 @@ class MachineErrorChecker(QObject):
self._keys_to_check = set() # type: Set[str]
self._num_keys_to_check_per_update = 10
def initialize(self) -> None:
self._error_check_timer.timeout.connect(self._rescheduleCheck)
@ -162,37 +164,37 @@ class MachineErrorChecker(QObject):
self._check_in_progress = True
# If there is nothing to check any more, it means there is no error.
if not self._stacks_and_keys_to_check:
# Finish
self._setResult(False)
return
for i in range(self._num_keys_to_check_per_update):
# If there is nothing to check any more, it means there is no error.
if not self._stacks_and_keys_to_check:
# Finish
self._setResult(False)
return
# Get the next stack and key to check
stack, key = self._stacks_and_keys_to_check.popleft()
# Get the next stack and key to check
stack, key = self._stacks_and_keys_to_check.popleft()
enabled = stack.getProperty(key, "enabled")
if not enabled:
self._application.callLater(self._checkStack)
return
enabled = stack.getProperty(key, "enabled")
if not enabled:
continue
validation_state = stack.getProperty(key, "validationState")
if validation_state is None:
# Setting is not validated. This can happen if there is only a setting definition.
# We do need to validate it, because a setting definitions value can be set by a function, which could
# be an invalid setting.
definition = stack.getSettingDefinition(key)
validator_type = SettingDefinition.getValidatorForType(definition.type)
if validator_type:
validator = validator_type(key)
validation_state = validator(stack)
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
# Since we don't know if any of the settings we didn't check is has an error value, store the list for the
# next check.
keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check}
keys_to_recheck.add(key)
self._setResult(True, keys_to_recheck = keys_to_recheck)
return
validation_state = stack.getProperty(key, "validationState")
if validation_state is None:
# Setting is not validated. This can happen if there is only a setting definition.
# We do need to validate it, because a setting definitions value can be set by a function, which could
# be an invalid setting.
definition = stack.getSettingDefinition(key)
validator_type = SettingDefinition.getValidatorForType(definition.type)
if validator_type:
validator = validator_type(key)
validation_state = validator(stack)
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
# Since we don't know if any of the settings we didn't check is has an error value, store the list for the
# next check.
keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check}
keys_to_recheck.add(key)
self._setResult(True, keys_to_recheck = keys_to_recheck)
continue
# Schedule the check for the next key
self._application.callLater(self._checkStack)

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for reading 3MF files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for writing 3MF files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,5 +3,5 @@
"author": "fieldOfView",
"version": "1.0.0",
"description": "Provides support for reading AMF files.",
"api": 7
"api": 8
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"description": "Backup and restore your configuration.",
"version": "1.2.0",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -2,7 +2,7 @@
"name": "CuraEngine Backend",
"author": "Ultimaker B.V.",
"description": "Provides the link to the CuraEngine slicing backend.",
"api": 7,
"api": 8,
"version": "1.0.1",
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for importing Cura profiles.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for exporting Cura profiles.",
"api": 7,
"api": 8,
"i18n-catalog":"cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"description": "Connects to the Digital Library, allowing Cura to open files from and save files to the Digital Library.",
"version": "1.1.0",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Checks for firmware updates.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a machine actions for updating firmware.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Reads g-code from a compressed archive.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Writes g-code to a compressed archive.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for importing profiles from g-code files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Victor Larchenko, Ultimaker B.V.",
"version": "1.0.1",
"description": "Allows loading and displaying G-code files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Writes g-code to a file.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Enables ability to generate printable geometry from 2D image files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for importing profiles from legacy Cura versions.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "fieldOfView, Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -0,0 +1,29 @@
from typing import Union
from cura import ApplicationMetadata
from cura.UltimakerCloud import UltimakerCloudConstants
class CloudApiModel:
sdk_version: Union[str, int] = ApplicationMetadata.CuraSDKVersion
cloud_api_version: str = UltimakerCloudConstants.CuraCloudAPIVersion
cloud_api_root: str = UltimakerCloudConstants.CuraCloudAPIRoot
api_url: str = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
cloud_api_root = cloud_api_root,
cloud_api_version = cloud_api_version,
sdk_version = sdk_version
)
# https://api.ultimaker.com/cura-packages/v1/user/packages
api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}/user/packages".format(
cloud_api_root = cloud_api_root,
cloud_api_version = cloud_api_version,
)
@classmethod
def userPackageUrl(cls, package_id: str) -> str:
"""https://api.ultimaker.com/cura-packages/v1/user/packages/{package_id}"""
return (CloudApiModel.api_url_user_packages + "/{package_id}").format(
package_id = package_id
)

View file

@ -0,0 +1,55 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from cura.CuraApplication import CuraApplication
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from ..CloudApiModel import CloudApiModel
class CloudApiClient:
"""Manages Cloud subscriptions
When a package is added to a user's account, the user is 'subscribed' to that package.
Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
Singleton: use CloudApiClient.getInstance() instead of CloudApiClient()
"""
__instance = None
@classmethod
def getInstance(cls, app: CuraApplication):
if not cls.__instance:
cls.__instance = CloudApiClient(app)
return cls.__instance
def __init__(self, app: CuraApplication) -> None:
if self.__instance is not None:
raise RuntimeError("This is a Singleton. use getInstance()")
self._scope: JsonDecoratorScope = JsonDecoratorScope(UltimakerCloudScope(app))
app.getPackageManager().packageInstalled.connect(self._onPackageInstalled)
def unsubscribe(self, package_id: str) -> None:
url = CloudApiModel.userPackageUrl(package_id)
HttpRequestManager.getInstance().delete(url = url, scope = self._scope)
def _subscribe(self, package_id: str) -> None:
"""You probably don't want to use this directly. All installed packages will be automatically subscribed."""
Logger.debug("Subscribing to {}", package_id)
data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version)
HttpRequestManager.getInstance().put(
url = CloudApiModel.api_url_user_packages,
data = data.encode(),
scope = self._scope
)
def _onPackageInstalled(self, package_id: str):
if CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
# We might already be subscribed, but checking would take one extra request. Instead, simply subscribe
self._subscribe(package_id)

View file

@ -0,0 +1,166 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import json
from typing import List, Dict, Any, Set
from typing import Optional
from PyQt6.QtCore import QObject
from PyQt6.QtNetwork import QNetworkReply
from UM import i18nCatalog
from UM.Logger import Logger
from UM.Message import Message
from UM.Signal import Signal
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from cura.API.Account import SyncState
from cura.CuraApplication import CuraApplication, ApplicationMetadata
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from .SubscribedPackagesModel import SubscribedPackagesModel
from ..CloudApiModel import CloudApiModel
class CloudPackageChecker(QObject):
SYNC_SERVICE_NAME = "CloudPackageChecker"
def __init__(self, application: CuraApplication) -> None:
super().__init__()
self.discrepancies = Signal() # Emits SubscribedPackagesModel
self._application: CuraApplication = application
self._scope = JsonDecoratorScope(UltimakerCloudScope(application))
self._model = SubscribedPackagesModel()
self._message: Optional[Message] = None
self._application.initializationFinished.connect(self._onAppInitialized)
self._i18n_catalog = i18nCatalog("cura")
self._sdk_version = ApplicationMetadata.CuraSDKVersion
self._last_notified_packages = set() # type: Set[str]
"""Packages for which a notification has been shown. No need to bother the user twice for equal content"""
# This is a plugin, so most of the components required are not ready when
# this is initialized. Therefore, we wait until the application is ready.
def _onAppInitialized(self) -> None:
self._package_manager = self._application.getPackageManager()
# initial check
self._getPackagesIfLoggedIn()
self._application.getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
self._application.getCuraAPI().account.syncRequested.connect(self._getPackagesIfLoggedIn)
def _onLoginStateChanged(self) -> None:
# reset session
self._last_notified_packages = set()
self._getPackagesIfLoggedIn()
def _getPackagesIfLoggedIn(self) -> None:
if self._application.getCuraAPI().account.isLoggedIn:
self._getUserSubscribedPackages()
else:
self._hideSyncMessage()
def _getUserSubscribedPackages(self) -> None:
self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SYNCING)
url = CloudApiModel.api_url_user_packages
self._application.getHttpRequestManager().get(url,
callback = self._onUserPackagesRequestFinished,
error_callback = self._onUserPackagesRequestFinished,
timeout = 10,
scope = self._scope)
def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None:
if error is not None or HttpRequestManager.safeHttpStatus(reply) != 200:
Logger.log("w",
"Requesting user packages failed, response code %s while trying to connect to %s",
HttpRequestManager.safeHttpStatus(reply), reply.url())
self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
return
try:
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
# Check for errors:
if "errors" in json_data:
for error in json_data["errors"]:
Logger.log("e", "%s", error["title"])
self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
return
self._handleCompatibilityData(json_data["data"])
except json.decoder.JSONDecodeError:
Logger.log("w", "Received invalid JSON for user subscribed packages from the Web Marketplace")
self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
user_subscribed_packages = {plugin["package_id"] for plugin in subscribed_packages_payload}
user_installed_packages = self._package_manager.getAllInstalledPackageIDs()
# We need to re-evaluate the dismissed packages
# (i.e. some package might got updated to the correct SDK version in the meantime,
# hence remove them from the Dismissed Incompatible list)
self._package_manager.reEvaluateDismissedPackages(subscribed_packages_payload, self._sdk_version)
user_dismissed_packages = self._package_manager.getDismissedPackages()
if user_dismissed_packages:
user_installed_packages.update(user_dismissed_packages)
# We check if there are packages installed in Web Marketplace but not in Cura marketplace
package_discrepancy = list(user_subscribed_packages.difference(user_installed_packages))
if user_subscribed_packages != self._last_notified_packages:
# scenario:
# 1. user subscribes to a package
# 2. dismisses the license/unsubscribes
# 3. subscribes to the same package again
# in this scenario we want to notify the user again. To capture that there was a change during
# step 2, we clear the last_notified after step 2. This way, the user will be notified after
# step 3 even though the list of packages for step 1 and 3 are equal
self._last_notified_packages = set()
if package_discrepancy:
account = self._application.getCuraAPI().account
account.setUpdatePackagesAction(lambda: self._onSyncButtonClicked(None, None))
if user_subscribed_packages == self._last_notified_packages:
# already notified user about these
return
Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages")
self._model.addDiscrepancies(package_discrepancy)
self._model.initialize(self._package_manager, subscribed_packages_payload)
self._showSyncMessage()
self._last_notified_packages = user_subscribed_packages
def _showSyncMessage(self) -> None:
"""Show the message if it is not already shown"""
if self._message is not None:
self._message.show()
return
sync_message = Message(self._i18n_catalog.i18nc(
"@info:generic",
"Do you want to sync material and software packages with your account?"),
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
sync_message.addAction("sync",
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
icon = "",
description = "Sync your plugins and print profiles to Ultimaker Cura.",
button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
sync_message.actionTriggered.connect(self._onSyncButtonClicked)
sync_message.show()
self._message = sync_message
def _hideSyncMessage(self) -> None:
"""Hide the message if it is showing"""
if self._message is not None:
self._message.hide()
self._message = None
def _onSyncButtonClicked(self, sync_message: Optional[Message], sync_message_action: Optional[str]) -> None:
if sync_message is not None:
sync_message.hide()
self._hideSyncMessage() # Should be the same message, but also sets _message to None
self.discrepancies.emit(self._model)

View file

@ -0,0 +1,44 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import Optional
from PyQt6.QtCore import QObject
from UM.Qt.QtApplication import QtApplication
from UM.Signal import Signal
from .SubscribedPackagesModel import SubscribedPackagesModel
class DiscrepanciesPresenter(QObject):
"""Shows a list of packages to be added or removed. The user can select which packages to (un)install. The user's
choices are emitted on the `packageMutations` Signal.
"""
def __init__(self, app: QtApplication) -> None:
super().__init__()
self.packageMutations = Signal() # Emits SubscribedPackagesModel
self._app = app
self._package_manager = app.getPackageManager()
self._dialog: Optional[QObject] = None
self._compatibility_dialog_path = "resources/qml/CompatibilityDialog.qml"
def present(self, plugin_path: str, model: SubscribedPackagesModel) -> None:
path = os.path.join(plugin_path, self._compatibility_dialog_path)
self._dialog = self._app.createQmlComponent(path, {"subscribedPackagesModel": model, "handler": self})
assert self._dialog
self._dialog.accepted.connect(lambda: self._onConfirmClicked(model))
def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None:
# If there are incompatible packages - automatically dismiss them
if model.getIncompatiblePackages():
self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages())
# For now, all compatible packages presented to the user should be installed.
# Later, we might remove items for which the user unselected the package
if model.getCompatiblePackages():
model.setItems(model.getCompatiblePackages())
self.packageMutations.emit(model)

View file

@ -0,0 +1,153 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import tempfile
from typing import Dict, List, Any
from PyQt6.QtNetwork import QNetworkReply
from UM.i18n import i18nCatalog
from UM.Logger import Logger
from UM.Message import Message
from UM.Signal import Signal
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from cura.CuraApplication import CuraApplication
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from .SubscribedPackagesModel import SubscribedPackagesModel
i18n_catalog = i18nCatalog("cura")
class DownloadPresenter:
"""Downloads a set of packages from the Ultimaker Cloud Marketplace
use download() exactly once: should not be used for multiple sets of downloads since this class contains state
"""
DISK_WRITE_BUFFER_SIZE = 256 * 1024 # 256 KB
def __init__(self, app: CuraApplication) -> None:
# Emits (Dict[str, str], List[str]) # (success_items, error_items)
# Dict{success_package_id, temp_file_path}
# List[errored_package_id]
self.done = Signal()
self._app = app
self._scope = UltimakerCloudScope(app)
self._started = False
self._progress_message = self._createProgressMessage()
self._progress: Dict[str, Dict[str, Any]] = {}
self._error: List[str] = []
def download(self, model: SubscribedPackagesModel) -> None:
if self._started:
Logger.error("Download already started. Create a new %s instead", self.__class__.__name__)
return
manager = HttpRequestManager.getInstance()
for item in model.items:
package_id = item["package_id"]
def finishedCallback(reply: QNetworkReply, pid = package_id) -> None:
self._onFinished(pid, reply)
def progressCallback(rx: int, rt: int, pid = package_id) -> None:
self._onProgress(pid, rx, rt)
def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, pid = package_id) -> None:
self._onError(pid)
request_data = manager.get(
item["download_url"],
callback = finishedCallback,
download_progress_callback = progressCallback,
error_callback = errorCallback,
scope = self._scope)
self._progress[package_id] = {
"received": 0,
"total": 1, # make sure this is not considered done yet. Also divByZero-safe
"file_written": None,
"request_data": request_data,
"package_model": item
}
self._started = True
self._progress_message.show()
def abort(self) -> None:
manager = HttpRequestManager.getInstance()
for item in self._progress.values():
manager.abortRequest(item["request_data"])
# Aborts all current operations and returns a copy with the same settings such as app and scope
def resetCopy(self) -> "DownloadPresenter":
self.abort()
self.done.disconnectAll()
return DownloadPresenter(self._app)
def _createProgressMessage(self) -> Message:
return Message(i18n_catalog.i18nc("@info:generic", "Syncing..."),
lifetime = 0,
use_inactivity_timer = False,
progress = 0.0,
title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account"))
def _onFinished(self, package_id: str, reply: QNetworkReply) -> None:
self._progress[package_id]["received"] = self._progress[package_id]["total"]
try:
with tempfile.NamedTemporaryFile(mode = "wb+", suffix = ".curapackage", delete = False) as temp_file:
bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
while bytes_read:
temp_file.write(bytes_read)
bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
self._app.processEvents()
self._progress[package_id]["file_written"] = temp_file.name
except IOError as e:
Logger.logException("e", "Failed to write downloaded package to temp file", e)
self._onError(package_id)
temp_file.close()
self._checkDone()
def _onProgress(self, package_id: str, rx: int, rt: int) -> None:
self._progress[package_id]["received"] = rx
self._progress[package_id]["total"] = rt
received = 0
total = 0
for item in self._progress.values():
received += item["received"]
total += item["total"]
if total == 0: # Total download size is 0, or unknown, or there are no progress items at all.
self._progress_message.setProgress(100.0)
return
self._progress_message.setProgress(100.0 * (received / total)) # [0 .. 100] %
def _onError(self, package_id: str) -> None:
self._progress.pop(package_id)
self._error.append(package_id)
self._checkDone()
def _checkDone(self) -> bool:
for item in self._progress.values():
if not item["file_written"]:
return False
success_items = {
package_id:
{
"package_path": value["file_written"],
"icon_url": value["package_model"]["icon_url"]
}
for package_id, value in self._progress.items()
}
error_items = [package_id for package_id in self._error]
self._progress_message.hide()
self.done.emit(success_items, error_items)
return True

View file

@ -0,0 +1,80 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
# Model for the ToolboxLicenseDialog
class LicenseModel(QObject):
DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline")
ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree")
dialogTitleChanged = pyqtSignal()
packageNameChanged = pyqtSignal()
licenseTextChanged = pyqtSignal()
iconChanged = pyqtSignal()
def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT, parent = None) -> None:
super().__init__(parent)
self._current_page_idx = 0
self._page_count = 1
self._dialogTitle = ""
self._license_text = ""
self._package_name = ""
self._icon_url = ""
self._decline_button_text = decline_button_text
@pyqtProperty(str, constant = True)
def acceptButtonText(self):
return self.ACCEPT_BUTTON_TEXT
@pyqtProperty(str, constant = True)
def declineButtonText(self):
return self._decline_button_text
@pyqtProperty(str, notify=dialogTitleChanged)
def dialogTitle(self) -> str:
return self._dialogTitle
@pyqtProperty(str, notify=packageNameChanged)
def packageName(self) -> str:
return self._package_name
def setPackageName(self, name: str) -> None:
self._package_name = name
self.packageNameChanged.emit()
@pyqtProperty(str, notify=iconChanged)
def iconUrl(self) -> str:
return self._icon_url
def setIconUrl(self, url: str):
self._icon_url = url
self.iconChanged.emit()
@pyqtProperty(str, notify=licenseTextChanged)
def licenseText(self) -> str:
return self._license_text
def setLicenseText(self, license_text: str) -> None:
if self._license_text != license_text:
self._license_text = license_text
self.licenseTextChanged.emit()
def setCurrentPageIdx(self, idx: int) -> None:
self._current_page_idx = idx
self._updateDialogTitle()
def setPageCount(self, count: int) -> None:
self._page_count = count
self._updateDialogTitle()
def _updateDialogTitle(self):
self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement")
if self._page_count > 1:
self._dialogTitle = self._dialogTitle + " ({}/{})".format(self._current_page_idx + 1, self._page_count)
self.dialogTitleChanged.emit()

View file

@ -0,0 +1,139 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import Dict, Optional, List, Any
from PyQt6.QtCore import QObject, pyqtSlot
from UM.Logger import Logger
from UM.PackageManager import PackageManager
from UM.Signal import Signal
from cura.CuraApplication import CuraApplication
from UM.i18n import i18nCatalog
from .LicenseModel import LicenseModel
class LicensePresenter(QObject):
"""Presents licenses for a set of packages for the user to accept or reject.
Call present() exactly once to show a licenseDialog for a set of packages
Before presenting another set of licenses, create a new instance using resetCopy().
licenseAnswers emits a list of Dicts containing answers when the user has made a choice for all provided packages.
"""
def __init__(self, app: CuraApplication) -> None:
super().__init__()
self._presented = False
"""Whether present() has been called and state is expected to be initialized"""
self._dialog: Optional[QObject] = None
self._package_manager: PackageManager = app.getPackageManager()
# Emits List[Dict[str, [Any]] containing for example
# [{ "package_id": "BarbarianPlugin", "package_path" : "/tmp/dg345as", "accepted" : True }]
self.licenseAnswers = Signal()
self._current_package_idx = 0
self._package_models: List[Dict] = []
self._catalog = i18nCatalog("cura")
decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account")
self._license_model: LicenseModel = LicenseModel(decline_button_text=decline_button_text)
self._page_count = 0
self._app = app
self._compatibility_dialog_path = "resources/qml/MultipleLicenseDialog.qml"
def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None:
"""Show a license dialog for multiple packages where users can read a license and accept or decline them
:param plugin_path: Root directory of the Toolbox plugin
:param packages: Dict[package id, file path]
"""
if self._presented:
Logger.error("{clazz} is single-use. Create a new {clazz} instead", clazz=self.__class__.__name__)
return
path = os.path.join(plugin_path, self._compatibility_dialog_path)
self._initState(packages)
if self._page_count == 0:
self.licenseAnswers.emit(self._package_models)
return
if self._dialog is None:
context_properties = {
"licenseModel": self._license_model,
"handler": self
}
self._dialog = self._app.createQmlComponent(path, context_properties)
self._presentCurrentPackage()
self._presented = True
def resetCopy(self) -> "LicensePresenter":
"""Clean up and return a new copy with the same settings such as app"""
if self._dialog:
self._dialog.close()
self.licenseAnswers.disconnectAll()
return LicensePresenter(self._app)
@pyqtSlot()
def onLicenseAccepted(self) -> None:
self._package_models[self._current_package_idx]["accepted"] = True
self._checkNextPage()
@pyqtSlot()
def onLicenseDeclined(self) -> None:
self._package_models[self._current_package_idx]["accepted"] = False
self._checkNextPage()
def _initState(self, packages: Dict[str, Dict[str, Any]]) -> None:
implicitly_accepted_count = 0
for package_id, item in packages.items():
item["package_id"] = package_id
try:
item["licence_content"] = self._package_manager.getPackageLicense(item["package_path"])
except EnvironmentError as e:
Logger.error(f"Could not open downloaded package {package_id} to read license file! {type(e)} - {e}")
continue # Skip this package.
if item["licence_content"] is None:
# Implicitly accept when there is no license
item["accepted"] = True
implicitly_accepted_count = implicitly_accepted_count + 1
self._package_models.append(item)
else:
item["accepted"] = None #: None: no answer yet
# When presenting the packages, we want to show packages which have a license first.
# In fact, we don't want to show the others at all because they are implicitly accepted
self._package_models.insert(0, item)
CuraApplication.getInstance().processEvents()
self._page_count = len(self._package_models) - implicitly_accepted_count
self._license_model.setPageCount(self._page_count)
def _presentCurrentPackage(self) -> None:
package_model = self._package_models[self._current_package_idx]
package_info = self._package_manager.getPackageInfo(package_model["package_path"])
self._license_model.setCurrentPageIdx(self._current_package_idx)
self._license_model.setPackageName(package_info["display_name"])
self._license_model.setIconUrl(package_model["icon_url"])
self._license_model.setLicenseText(package_model["licence_content"])
if self._dialog:
self._dialog.open() # Does nothing if already open
def _checkNextPage(self) -> None:
if self._current_package_idx + 1 < self._page_count:
self._current_package_idx += 1
self._presentCurrentPackage()
else:
if self._dialog:
self._dialog.close()
self.licenseAnswers.emit(self._package_models)

View file

@ -0,0 +1,35 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM import i18nCatalog
from UM.Message import Message
from cura.CuraApplication import CuraApplication
class RestartApplicationPresenter:
"""Presents a dialog telling the user that a restart is required to apply changes
Since we cannot restart Cura, the app is closed instead when the button is clicked
"""
def __init__(self, app: CuraApplication) -> None:
self._app = app
self._i18n_catalog = i18nCatalog("cura")
def present(self) -> None:
app_name = self._app.getApplicationDisplayName()
message = Message(self._i18n_catalog.i18nc("@info:generic",
"You need to quit and restart {} before changes have effect.",
app_name))
message.addAction("quit",
name="Quit " + app_name,
icon = "",
description="Close the application",
button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
message.actionTriggered.connect(self._quitClicked)
message.show()
def _quitClicked(self, *_):
self._app.windowClosed()

View file

@ -0,0 +1,74 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt6.QtCore import Qt, pyqtProperty
from UM.PackageManager import PackageManager
from UM.Qt.ListModel import ListModel
from UM.Version import Version
from cura import ApplicationMetadata
from typing import List, Dict, Any
class SubscribedPackagesModel(ListModel):
def __init__(self, parent = None):
super().__init__(parent)
self._items = []
self._metadata = None
self._discrepancies = None
self._sdk_version = ApplicationMetadata.CuraSDKVersion
self.addRoleName(Qt.ItemDataRole.UserRole + 1, "package_id")
self.addRoleName(Qt.ItemDataRole.UserRole + 2, "display_name")
self.addRoleName(Qt.ItemDataRole.UserRole + 3, "icon_url")
self.addRoleName(Qt.ItemDataRole.UserRole + 4, "is_compatible")
self.addRoleName(Qt.ItemDataRole.UserRole + 5, "is_dismissed")
@pyqtProperty(bool, constant=True)
def hasCompatiblePackages(self) -> bool:
for item in self._items:
if item['is_compatible']:
return True
return False
@pyqtProperty(bool, constant=True)
def hasIncompatiblePackages(self) -> bool:
for item in self._items:
if not item['is_compatible']:
return True
return False
def addDiscrepancies(self, discrepancy: List[str]) -> None:
self._discrepancies = discrepancy
def getCompatiblePackages(self) -> List[Dict[str, Any]]:
return [package for package in self._items if package["is_compatible"]]
def getIncompatiblePackages(self) -> List[str]:
return [package["package_id"] for package in self._items if not package["is_compatible"]]
def initialize(self, package_manager: PackageManager, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
self._items.clear()
for item in subscribed_packages_payload:
if item["package_id"] not in self._discrepancies:
continue
package = {
"package_id": item["package_id"],
"display_name": item["display_name"],
"sdk_versions": item["sdk_versions"],
"download_url": item["download_url"],
"md5_hash": item["md5_hash"],
"is_dismissed": False,
}
compatible = any(package_manager.isPackageCompatible(Version(version)) for version in item["sdk_versions"])
package.update({"is_compatible": compatible})
try:
package.update({"icon_url": item["icon_url"]})
except KeyError: # There is no 'icon_url" in the response payload for this package
package.update({"icon_url": ""})
self._items.append(package)
self.setItems(self._items)

View file

@ -0,0 +1,114 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import List, Dict, Any, cast
from UM import i18n_catalog
from UM.Extension import Extension
from UM.Logger import Logger
from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from cura.CuraApplication import CuraApplication
from .CloudPackageChecker import CloudPackageChecker
from .CloudApiClient import CloudApiClient
from .DiscrepanciesPresenter import DiscrepanciesPresenter
from .DownloadPresenter import DownloadPresenter
from .LicensePresenter import LicensePresenter
from .RestartApplicationPresenter import RestartApplicationPresenter
from .SubscribedPackagesModel import SubscribedPackagesModel
class SyncOrchestrator(Extension):
"""Orchestrates the synchronizing of packages from the user account to the installed packages
Example flow:
- CloudPackageChecker compares a list of packages the user `subscribed` to in their account
If there are `discrepancies` between the account and locally installed packages, they are emitted
- DiscrepanciesPresenter shows a list of packages to be added or removed to the user. It emits the `packageMutations`
the user selected to be performed
- The SyncOrchestrator uses PackageManager to remove local packages the users wants to see removed
- The DownloadPresenter shows a download progress dialog. It emits A tuple of succeeded and failed downloads
- The LicensePresenter extracts licenses from the downloaded packages and presents a license for each package to
be installed. It emits the `licenseAnswers` signal for accept or declines
- The CloudApiClient removes the declined packages from the account
- The SyncOrchestrator uses PackageManager to install the downloaded packages and delete temp files.
- The RestartApplicationPresenter notifies the user that a restart is required for changes to take effect
"""
def __init__(self, app: CuraApplication) -> None:
super().__init__()
# Differentiate This PluginObject from the Marketplace. self.getId() includes _name.
# getPluginId() will return the same value for The Marketplace extension and this one
self._name = "SyncOrchestrator"
self._package_manager = app.getPackageManager()
# Keep a reference to the CloudApiClient. it watches for installed packages and subscribes to them
self._cloud_api: CloudApiClient = CloudApiClient.getInstance(app)
self._checker: CloudPackageChecker = CloudPackageChecker(app)
self._checker.discrepancies.connect(self._onDiscrepancies)
self._discrepancies_presenter: DiscrepanciesPresenter = DiscrepanciesPresenter(app)
self._discrepancies_presenter.packageMutations.connect(self._onPackageMutations)
self._download_presenter: DownloadPresenter = DownloadPresenter(app)
self._license_presenter: LicensePresenter = LicensePresenter(app)
self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers)
self._restart_presenter = RestartApplicationPresenter(app)
def _onDiscrepancies(self, model: SubscribedPackagesModel) -> None:
plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId()))
self._discrepancies_presenter.present(plugin_path, model)
def _onPackageMutations(self, mutations: SubscribedPackagesModel) -> None:
self._download_presenter = self._download_presenter.resetCopy()
self._download_presenter.done.connect(self._onDownloadFinished)
self._download_presenter.download(mutations)
def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None:
"""Called when a set of packages have finished downloading
:param success_items:: Dict[package_id, Dict[str, str]]
:param error_items:: List[package_id]
"""
if error_items:
message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items)))
self._showErrorMessage(message)
plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId()))
self._license_presenter = self._license_presenter.resetCopy()
self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers)
self._license_presenter.present(plugin_path, success_items)
# Called when user has accepted / declined all licenses for the downloaded packages
def _onLicenseAnswers(self, answers: List[Dict[str, Any]]) -> None:
has_changes = False # True when at least one package is installed
for item in answers:
if item["accepted"]:
# install and subscribe packages
if not self._package_manager.installPackage(item["package_path"]):
message = "Could not install {}".format(item["package_id"])
self._showErrorMessage(message)
continue
has_changes = True
else:
self._cloud_api.unsubscribe(item["package_id"])
# delete temp file
try:
os.remove(item["package_path"])
except EnvironmentError as e: # File was already removed, no access rights, etc.
Logger.error("Can't delete temporary package file: {err}".format(err = str(e)))
if has_changes:
self._restart_presenter.present()
def _showErrorMessage(self, text: str):
"""Logs an error and shows it to the user"""
Logger.error(text)
Message(text, lifetime = 0, message_type = Message.MessageType.ERROR).show()

View file

@ -1,6 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from .CloudSync.SyncOrchestrator import SyncOrchestrator
from .Marketplace import Marketplace
def getMetaData():
@ -14,4 +14,4 @@ def register(app):
"""
Register the plug-in object with Uranium.
"""
return { "extension": Marketplace() }
return { "extension": [Marketplace(), SyncOrchestrator(app)] }

View file

@ -2,7 +2,7 @@
"name": "Marketplace",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"api": 7,
"api": 8,
"description": "Manages extensions to the application and allows browsing extensions from the Ultimaker website.",
"i18n-catalog": "cura"
}

View file

@ -0,0 +1,146 @@
// Copyright (c) 2022 Ultimaker B.V.
// Marketplace is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import UM 1.5 as UM
import Cura 1.6 as Cura
UM.Dialog
{
visible: true
title: catalog.i18nc("@title", "Changes from your account")
width: UM.Theme.getSize("popup_dialog").width
height: UM.Theme.getSize("popup_dialog").height
minimumWidth: width
maximumWidth: minimumWidth
minimumHeight: height
maximumHeight: minimumHeight
margin: 0
property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? catalog.i18nc("@button", "Dismiss") : catalog.i18nc("@button", "Next")
Rectangle
{
id: root
anchors.fill: parent
color: UM.Theme.getColor("main_background")
UM.I18nCatalog
{
id: catalog
name: "cura"
}
ScrollView
{
width: parent.width
height: parent.height - nextButton.height - nextButton.anchors.margins * 2 // We want some leftover space for the button at the bottom
clip: true
Column
{
anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width
// Compatible packages
UM.Label
{
text: catalog.i18nc("@label", "The following packages will be added:")
visible: subscribedPackagesModel.hasCompatiblePackages
height: contentHeight + UM.Theme.getSize("default_margin").height
}
Repeater
{
model: subscribedPackagesModel
Component
{
Item
{
width: parent.width
property int lineHeight: 60
visible: model.is_compatible
height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the compatible packages here
Image
{
id: packageIcon
source: model.icon_url || Qt.resolvedUrl("../../images/placeholder.svg")
height: lineHeight
width: height
sourceSize.height: height
sourceSize.width: width
mipmap: true
fillMode: Image.PreserveAspectFit
}
UM.Label
{
text: model.display_name
font: UM.Theme.getFont("medium_bold")
anchors.left: packageIcon.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: packageIcon.verticalCenter
elide: Text.ElideRight
}
}
}
}
// Incompatible packages
UM.Label
{
text: catalog.i18nc("@label", "The following packages can not be installed because of an incompatible Cura version:")
visible: subscribedPackagesModel.hasIncompatiblePackages
height: contentHeight + UM.Theme.getSize("default_margin").height
}
Repeater
{
model: subscribedPackagesModel
Component
{
Item
{
width: parent.width
property int lineHeight: 60
visible: !model.is_compatible && !model.is_dismissed
height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the incompatible packages here
Image
{
id: packageIcon
source: model.icon_url || Qt.resolvedUrl("../../images/placeholder.svg")
height: lineHeight
width: height
sourceSize.height: height
sourceSize.width: width
mipmap: true
fillMode: Image.PreserveAspectFit
}
UM.Label
{
text: model.display_name
font: UM.Theme.getFont("medium_bold")
anchors.left: packageIcon.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: packageIcon.verticalCenter
elide: Text.ElideRight
}
}
}
}
}
} // End of ScrollView
Cura.PrimaryButton
{
id: nextButton
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").height
text: actionButtonText
onClicked: accept()
}
}
}

View file

@ -0,0 +1,98 @@
// Copyright (c) 2021 Ultimaker B.V.
// Marketplace is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import UM 1.5 as UM
import Cura 1.6 as Cura
UM.Dialog
{
id: licenseDialog
title: licenseModel.dialogTitle
minimumWidth: UM.Theme.getSize("modal_window_minimum").width
minimumHeight: UM.Theme.getSize("modal_window_minimum").height
width: minimumWidth
height: minimumHeight
backgroundColor: UM.Theme.getColor("main_background")
margin: UM.Theme.getSize("default_margin").width
ColumnLayout
{
anchors.fill: parent
spacing: UM.Theme.getSize("thick_margin").height
UM.I18nCatalog { id: catalog; name: "cura" }
UM.Label
{
id: licenseHeader
Layout.fillWidth: true
text: catalog.i18nc("@label", "You need to accept the license to install the package")
}
Row {
id: packageRow
Layout.fillWidth: true
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
leftPadding: UM.Theme.getSize("narrow_margin").width
Image
{
id: icon
width: UM.Theme.getSize("card_icon").width
height: width
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
source: licenseModel.iconUrl || Qt.resolvedUrl("../../images/placeholder.svg")
mipmap: true
}
UM.Label
{
id: packageName
text: licenseModel.packageName
font.bold: true
anchors.verticalCenter: icon.verticalCenter
height: contentHeight
}
}
Cura.ScrollableTextArea
{
Layout.fillWidth: true
Layout.fillHeight: true
anchors.topMargin: UM.Theme.getSize("default_margin").height
textArea.text: licenseModel.licenseText
textArea.readOnly: true
}
}
rightButtons:
[
Cura.PrimaryButton
{
text: licenseModel.acceptButtonText
onClicked: handler.onLicenseAccepted()
}
]
leftButtons:
[
Cura.SecondaryButton
{
id: declineButton
text: licenseModel.declineButtonText
onClicked: handler.onLicenseDeclined()
}
]
}

View file

@ -19,7 +19,6 @@ Rectangle
implicitHeight: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height
color: UM.Theme.getColor("action_panel_secondary")
// Icon
UM.ColorImage
{
id: onboardingIcon
@ -33,7 +32,6 @@ Rectangle
height: UM.Theme.getSize("banner_icon_size").height
}
// Close button
UM.SimpleButton
{
id: onboardingClose
@ -52,8 +50,8 @@ Rectangle
onClicked: onRemove()
}
// Body
Label {
UM.Label
{
id: infoText
anchors
{
@ -63,11 +61,7 @@ Rectangle
margins: UM.Theme.getSize("default_margin").width
}
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
color: UM.Theme.getColor("primary_text")
wrapMode: Text.Wrap
elide: Text.ElideRight
onLineLaidOut: (line) =>
@ -102,7 +96,7 @@ Rectangle
id: readMoreButton
anchors.left: infoText.left
anchors.bottom: infoText.bottom
text: "Learn More"
text: catalog.i18nc("@button:label", "Learn More")
textFont: UM.Theme.getFont("default")
textColor: infoText.color
leftPadding: 0

View file

@ -2,7 +2,7 @@
"name": "Model Checker",
"author": "Ultimaker B.V.",
"version": "1.0.1",
"api": 7,
"api": 8,
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a monitor stage in Cura.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -196,7 +196,7 @@ Item
height: parent.height
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("default_margin").width
ScrollBar.vertical: UM.ScrollBar {}
ScrollBar.vertical: UM.ScrollBar { id: scrollBar }
clip: true
spacing: UM.Theme.getSize("default_lining").height
@ -244,7 +244,7 @@ Item
Loader
{
id: settingLoader
width: UM.Theme.getSize("setting").width - removeButton.width
width: UM.Theme.getSize("setting").width - removeButton.width - scrollBar.width
height: UM.Theme.getSize("section").height + UM.Theme.getSize("narrow_margin").height
enabled: provider.properties.enabled === "True"
property var definition: model
@ -299,7 +299,7 @@ Item
{
id: removeButton
width: UM.Theme.getSize("setting").height
height: UM.Theme.getSize("setting").height
height: UM.Theme.getSize("setting").height + UM.Theme.getSize("narrow_margin").height
onClicked: addedSettingsModel.setVisible(model.key, false)

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides the Per Model Settings.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -2,7 +2,7 @@
"name": "Post Processing",
"author": "Ultimaker",
"version": "2.2.1",
"api": 7,
"api": 8,
"description": "Extension that allows for user created scripts for post processing",
"catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a prepare stage in Cura.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a preview stage in Cura.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"description": "Provides removable drive hotplugging and writing support.",
"version": "1.0.1",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Logs certain events so that they can be used by the crash reporter",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides the preview of sliced layerdata.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Submits anonymous slice info. Can be disabled through preferences.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides a normal solid mesh view.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Creates an eraser mesh to block the printing of support in certain places",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,5 +3,5 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides support for reading model files.",
"api": 7
"api": 8
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides support for reading Ultimaker Format Packages.",
"supported_sdk_versions": ["7.9.0"],
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for writing Ultimaker Format Packages.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"description": "Manages network connections to Ultimaker networked printers.",
"version": "2.0.0",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -2,7 +2,7 @@
"name": "USB printing",
"author": "Ultimaker B.V.",
"version": "1.0.2",
"api": 7,
"api": 8,
"description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.",
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 2.1 to Cura 2.2.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 2.2 to Cura 2.4.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 2.5 to Cura 2.6.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 2.6 to Cura 2.7.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 2.7 to Cura 3.0.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 3.0 to Cura 3.1.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 3.5 to Cura 4.0.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Upgrades configurations from Cura 4.0 to Cura 4.1.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.11 to Cura 4.12.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.13 to Cura 5.0.",
"api": "7.9.0",
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.1 to Cura 4.2.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.2 to Cura 4.3.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.3 to Cura 4.4.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.4 to Cura 4.5.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.5 to Cura 4.6.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.6.2 to Cura 4.7.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.7 to Cura 4.8.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.8 to Cura 4.9.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.9 to Cura 4.10.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Seva Alekseyev, Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides support for reading X3D files.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides the X-Ray view.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides capabilities to read and write XML-based material profiles.",
"api": 7,
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -6,7 +6,7 @@
"display_name": "3MF Reader",
"description": "Provides support for reading 3MF files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -23,7 +23,7 @@
"display_name": "3MF Writer",
"description": "Provides support for writing 3MF files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -40,7 +40,7 @@
"display_name": "AMF Reader",
"description": "Provides support for reading AMF files.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "fieldOfView",
@ -57,7 +57,7 @@
"display_name": "Cura Backups",
"description": "Backup and restore your configuration.",
"package_version": "1.2.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -74,7 +74,7 @@
"display_name": "CuraEngine Backend",
"description": "Provides the link to the CuraEngine slicing backend.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -91,7 +91,7 @@
"display_name": "Cura Profile Reader",
"description": "Provides support for importing Cura profiles.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -108,7 +108,7 @@
"display_name": "Cura Profile Writer",
"description": "Provides support for exporting Cura profiles.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -125,7 +125,7 @@
"display_name": "Ultimaker Digital Library",
"description": "Connects to the Digital Library, allowing Cura to open files from and save files to the Digital Library.",
"package_version": "1.1.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -142,7 +142,7 @@
"display_name": "Firmware Update Checker",
"description": "Checks for firmware updates.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -159,7 +159,7 @@
"display_name": "Firmware Updater",
"description": "Provides a machine actions for updating firmware.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -176,7 +176,7 @@
"display_name": "Compressed G-code Reader",
"description": "Reads g-code from a compressed archive.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -193,7 +193,7 @@
"display_name": "Compressed G-code Writer",
"description": "Writes g-code to a compressed archive.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -210,7 +210,7 @@
"display_name": "G-Code Profile Reader",
"description": "Provides support for importing profiles from g-code files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -227,7 +227,7 @@
"display_name": "G-Code Reader",
"description": "Allows loading and displaying G-code files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "VictorLarchenko",
@ -244,7 +244,7 @@
"display_name": "G-Code Writer",
"description": "Writes g-code to a file.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -261,7 +261,7 @@
"display_name": "Image Reader",
"description": "Enables ability to generate printable geometry from 2D image files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -278,7 +278,7 @@
"display_name": "Legacy Cura Profile Reader",
"description": "Provides support for importing profiles from legacy Cura versions.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -295,7 +295,7 @@
"display_name": "Machine Settings Action",
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "fieldOfView",
@ -312,7 +312,7 @@
"display_name": "Model Checker",
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -329,7 +329,7 @@
"display_name": "Monitor Stage",
"description": "Provides a monitor stage in Cura.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -346,7 +346,7 @@
"display_name": "Per-Object Settings Tool",
"description": "Provides the per-model settings.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -363,7 +363,7 @@
"display_name": "Post Processing",
"description": "Extension that allows for user created scripts for post processing.",
"package_version": "2.2.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -380,7 +380,7 @@
"display_name": "Prepare Stage",
"description": "Provides a prepare stage in Cura.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -397,7 +397,7 @@
"display_name": "Preview Stage",
"description": "Provides a preview stage in Cura.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -414,7 +414,7 @@
"display_name": "Removable Drive Output Device",
"description": "Provides removable drive hotplugging and writing support.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -431,7 +431,7 @@
"display_name": "Sentry Logger",
"description": "Logs certain events so that they can be used by the crash reporter",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -448,7 +448,7 @@
"display_name": "Simulation View",
"description": "Provides the Simulation view.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -465,7 +465,7 @@
"display_name": "Slice Info",
"description": "Submits anonymous slice info. Can be disabled through preferences.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -482,7 +482,7 @@
"display_name": "Solid View",
"description": "Provides a normal solid mesh view.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -499,7 +499,7 @@
"display_name": "Support Eraser Tool",
"description": "Creates an eraser mesh to block the printing of support in certain places.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -516,7 +516,7 @@
"display_name": "Trimesh Reader",
"description": "Provides support for reading model files.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -533,7 +533,7 @@
"display_name": "Marketplace",
"description": "Find, manage and install new Cura packages.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -550,7 +550,7 @@
"display_name": "UFP Reader",
"description": "Provides support for reading Ultimaker Format Packages.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -567,7 +567,7 @@
"display_name": "UFP Writer",
"description": "Provides support for writing Ultimaker Format Packages.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -584,7 +584,7 @@
"display_name": "Ultimaker Machine Actions",
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -601,7 +601,7 @@
"display_name": "UM3 Network Printing",
"description": "Manages network connections to Ultimaker 3 printers.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -618,7 +618,7 @@
"display_name": "USB Printing",
"description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.",
"package_version": "1.0.2",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -635,7 +635,7 @@
"display_name": "Version Upgrade 2.1 to 2.2",
"description": "Upgrades configurations from Cura 2.1 to Cura 2.2.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -652,7 +652,7 @@
"display_name": "Version Upgrade 2.2 to 2.4",
"description": "Upgrades configurations from Cura 2.2 to Cura 2.4.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -669,7 +669,7 @@
"display_name": "Version Upgrade 2.5 to 2.6",
"description": "Upgrades configurations from Cura 2.5 to Cura 2.6.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -686,7 +686,7 @@
"display_name": "Version Upgrade 2.6 to 2.7",
"description": "Upgrades configurations from Cura 2.6 to Cura 2.7.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -703,7 +703,7 @@
"display_name": "Version Upgrade 2.7 to 3.0",
"description": "Upgrades configurations from Cura 2.7 to Cura 3.0.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -720,7 +720,7 @@
"display_name": "Version Upgrade 3.0 to 3.1",
"description": "Upgrades configurations from Cura 3.0 to Cura 3.1.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -737,7 +737,7 @@
"display_name": "Version Upgrade 3.2 to 3.3",
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -754,7 +754,7 @@
"display_name": "Version Upgrade 3.3 to 3.4",
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -771,7 +771,7 @@
"display_name": "Version Upgrade 3.4 to 3.5",
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -788,7 +788,7 @@
"display_name": "Version Upgrade 3.5 to 4.0",
"description": "Upgrades configurations from Cura 3.5 to Cura 4.0.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -805,7 +805,7 @@
"display_name": "Version Upgrade 4.0 to 4.1",
"description": "Upgrades configurations from Cura 4.0 to Cura 4.1.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -822,7 +822,7 @@
"display_name": "Version Upgrade 4.1 to 4.2",
"description": "Upgrades configurations from Cura 4.1 to Cura 4.2.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -839,7 +839,7 @@
"display_name": "Version Upgrade 4.2 to 4.3",
"description": "Upgrades configurations from Cura 4.2 to Cura 4.3.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -856,7 +856,7 @@
"display_name": "Version Upgrade 4.3 to 4.4",
"description": "Upgrades configurations from Cura 4.3 to Cura 4.4.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -873,7 +873,7 @@
"display_name": "Version Upgrade 4.4 to 4.5",
"description": "Upgrades configurations from Cura 4.4 to Cura 4.5.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -890,7 +890,7 @@
"display_name": "Version Upgrade 4.5 to 4.6",
"description": "Upgrades configurations from Cura 4.5 to Cura 4.6.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -907,7 +907,7 @@
"display_name": "Version Upgrade 4.6.0 to 4.6.2",
"description": "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -924,7 +924,7 @@
"display_name": "Version Upgrade 4.6.2 to 4.7",
"description": "Upgrades configurations from Cura 4.6.2 to Cura 4.7.",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -941,7 +941,7 @@
"display_name": "Version Upgrade 4.7.0 to 4.8.0",
"description": "Upgrades configurations from Cura 4.7.0 to Cura 4.8.0",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -958,7 +958,7 @@
"display_name": "Version Upgrade 4.8.0 to 4.9.0",
"description": "Upgrades configurations from Cura 4.8.0 to Cura 4.9.0",
"package_version": "1.0.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -975,8 +975,7 @@
"display_name": "Version Upgrade 4.9 to 4.10",
"description": "Upgrades configurations from Cura 4.9 to Cura 4.10",
"package_version": "1.0.0",
"sdk_version": 7,
"sdk_version_semver": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -1011,7 +1010,7 @@
"display_name": "X3D Reader",
"description": "Provides support for reading X3D files.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "SevaAlekseyev",
@ -1028,7 +1027,7 @@
"display_name": "XML Material Profiles",
"description": "Provides capabilities to read and write XML-based material profiles.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -1045,7 +1044,7 @@
"display_name": "X-Ray View",
"description": "Provides the X-Ray view.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
@ -1062,7 +1061,7 @@
"display_name": "Generic ABS",
"description": "The generic ABS profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1080,7 +1079,7 @@
"display_name": "Generic BAM",
"description": "The generic BAM profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1098,7 +1097,7 @@
"display_name": "Generic CFF CPE",
"description": "The generic CFF CPE profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1116,7 +1115,7 @@
"display_name": "Generic CFF PA",
"description": "The generic CFF PA profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1134,7 +1133,7 @@
"display_name": "Generic CPE",
"description": "The generic CPE profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1152,7 +1151,7 @@
"display_name": "Generic CPE+",
"description": "The generic CPE+ profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1170,7 +1169,7 @@
"display_name": "Generic GFF CPE",
"description": "The generic GFF CPE profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1188,7 +1187,7 @@
"display_name": "Generic GFF PA",
"description": "The generic GFF PA profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1206,7 +1205,7 @@
"display_name": "Generic HIPS",
"description": "The generic HIPS profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1224,7 +1223,7 @@
"display_name": "Generic Nylon",
"description": "The generic Nylon profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1242,7 +1241,7 @@
"display_name": "Generic PC",
"description": "The generic PC profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1260,7 +1259,7 @@
"display_name": "Generic PETG",
"description": "The generic PETG profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1278,7 +1277,7 @@
"display_name": "Generic PLA",
"description": "The generic PLA profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1296,7 +1295,7 @@
"display_name": "Generic PP",
"description": "The generic PP profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1314,7 +1313,7 @@
"display_name": "Generic PVA",
"description": "The generic PVA profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1332,7 +1331,7 @@
"display_name": "Generic Tough PLA",
"description": "The generic Tough PLA profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1350,7 +1349,7 @@
"display_name": "Generic TPU",
"description": "The generic TPU profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
@ -1368,7 +1367,7 @@
"display_name": "Dagoma Chromatik PLA",
"description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://dagoma.fr/boutique/filaments.html",
"author": {
"author_id": "Dagoma",
@ -1385,7 +1384,7 @@
"display_name": "FABtotum ABS",
"description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so its compatible with all versions of the FABtotum Personal fabricator.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40",
"author": {
"author_id": "FABtotum",
@ -1402,7 +1401,7 @@
"display_name": "FABtotum Nylon",
"description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53",
"author": {
"author_id": "FABtotum",
@ -1419,7 +1418,7 @@
"display_name": "FABtotum PLA",
"description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starchs sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotums one is between 185° and 195°.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39",
"author": {
"author_id": "FABtotum",
@ -1436,7 +1435,7 @@
"display_name": "FABtotum TPU Shore 98A",
"description": "",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66",
"author": {
"author_id": "FABtotum",
@ -1453,7 +1452,7 @@
"display_name": "Fiberlogy HD PLA",
"description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/",
"author": {
"author_id": "Fiberlogy",
@ -1470,7 +1469,7 @@
"display_name": "Filo3D PLA",
"description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://dagoma.fr",
"author": {
"author_id": "Dagoma",
@ -1487,7 +1486,7 @@
"display_name": "IMADE3D JellyBOX PETG",
"description": "",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://shop.imade3d.com/filament.html",
"author": {
"author_id": "IMADE3D",
@ -1504,7 +1503,7 @@
"display_name": "IMADE3D JellyBOX PLA",
"description": "",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://shop.imade3d.com/filament.html",
"author": {
"author_id": "IMADE3D",
@ -1521,7 +1520,7 @@
"display_name": "Octofiber PLA",
"description": "PLA material from Octofiber.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://nl.octofiber.com/3d-printing-filament/pla.html",
"author": {
"author_id": "Octofiber",
@ -1538,7 +1537,7 @@
"display_name": "PolyFlex™ PLA",
"description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://www.polymaker.com/shop/polyflex/",
"author": {
"author_id": "Polymaker",
@ -1555,7 +1554,7 @@
"display_name": "PolyMax™ PLA",
"description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://www.polymaker.com/shop/polymax/",
"author": {
"author_id": "Polymaker",
@ -1572,7 +1571,7 @@
"display_name": "PolyPlus™ PLA True Colour",
"description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://www.polymaker.com/shop/polyplus-true-colour/",
"author": {
"author_id": "Polymaker",
@ -1589,7 +1588,7 @@
"display_name": "PolyWood™ PLA",
"description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.",
"package_version": "1.0.1",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "http://www.polymaker.com/shop/polywood/",
"author": {
"author_id": "Polymaker",
@ -1606,7 +1605,7 @@
"display_name": "Ultimaker ABS",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "UltimakerPackages",
@ -1625,7 +1624,7 @@
"display_name": "Ultimaker Breakaway",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/breakaway",
"author": {
"author_id": "UltimakerPackages",
@ -1644,7 +1643,7 @@
"display_name": "Ultimaker CPE",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "UltimakerPackages",
@ -1663,7 +1662,7 @@
"display_name": "Ultimaker CPE+",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/cpe",
"author": {
"author_id": "UltimakerPackages",
@ -1682,7 +1681,7 @@
"display_name": "Ultimaker Nylon",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "UltimakerPackages",
@ -1701,7 +1700,7 @@
"display_name": "Ultimaker PC",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/pc",
"author": {
"author_id": "UltimakerPackages",
@ -1720,7 +1719,7 @@
"display_name": "Ultimaker PLA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "UltimakerPackages",
@ -1739,7 +1738,7 @@
"display_name": "Ultimaker PP",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/pp",
"author": {
"author_id": "UltimakerPackages",
@ -1758,7 +1757,7 @@
"display_name": "Ultimaker PVA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "UltimakerPackages",
@ -1777,7 +1776,7 @@
"display_name": "Ultimaker TPU 95A",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/tpu-95a",
"author": {
"author_id": "UltimakerPackages",
@ -1796,7 +1795,7 @@
"display_name": "Ultimaker Tough PLA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://ultimaker.com/products/materials/tough-pla",
"author": {
"author_id": "UltimakerPackages",
@ -1815,7 +1814,7 @@
"display_name": "Vertex Delta ABS",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1832,7 +1831,7 @@
"display_name": "Vertex Delta PET",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1849,7 +1848,7 @@
"display_name": "Vertex Delta PLA",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1866,7 +1865,7 @@
"display_name": "Vertex Delta TPU",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.4.0",
"sdk_version": "7.9.0",
"sdk_version": "8.0.0",
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",

View file

@ -13,31 +13,38 @@ Row // Sync state icon + message
height: childrenRect.height
spacing: UM.Theme.getSize("narrow_margin").height
// These are the enums from cura/API/account.py
// somehow exposing these enums from python to QML doesn't work properly anymore
property var _Cura_AccountSyncState_SYNCING: 0
property var _Cura_AccountSyncState_SUCCESS: 1
property var _Cura_AccountSyncState_ERROR: 2
property var _Cura_AccountSyncState_IDLE: 3
states: [
State
{
name: "idle"
when: syncState == Cura.AccountSyncState.IDLE
when: syncState == _Cura_AccountSyncState_IDLE
PropertyChanges { target: icon; source: UM.Theme.getIcon("ArrowDoubleCircleRight")}
},
State
{
name: "syncing"
when: syncState == Cura.AccountSyncState.SYNCING
when: syncState == _Cura_AccountSyncState_SYNCING
PropertyChanges { target: icon; source: UM.Theme.getIcon("ArrowDoubleCircleRight") }
PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Checking...")}
},
State
{
name: "up_to_date"
when: syncState == Cura.AccountSyncState.SUCCESS
when: syncState == _Cura_AccountSyncState_SUCCESS
PropertyChanges { target: icon; source: UM.Theme.getIcon("CheckCircle") }
PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Account synced")}
},
State
{
name: "error"
when: syncState == Cura.AccountSyncState.ERROR
when: syncState == _Cura_AccountSyncState_ERROR
PropertyChanges { target: icon; source: UM.Theme.getIcon("Warning") }
PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Something went wrong...")}
}

View file

@ -46,7 +46,7 @@ Column
{
id: initialLabel
anchors.centerIn: parent
text: profile["username"].charAt(0).toUpperCase()
text: profile.username.charAt(0).toUpperCase()
font: UM.Theme.getFont("large_bold")
horizontalAlignment: Text.AlignHCenter
}

View file

@ -28,7 +28,7 @@ Cura.Menu
Cura.MachineManager.switchPrinterType(modelData.machine_type)
}
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
onObjectAdded: function(index, object) { return menu.insertItem(index, object); }
onObjectRemoved: function(object) { return menu.removeItem(object); }
}
}

View file

@ -172,7 +172,8 @@ Item
// same operation
const active_mode = UM.Preferences.getValue("cura/active_mode")
if (active_mode == 0 || active_mode == "simple")
if (visible // Workaround: 'visible' is checked because on startup in Windows it spuriously gets an 'onValueChanged' with value '0' if this isn't checked.
&& (active_mode == 0 || active_mode == "simple"))
{
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance")

View file

@ -11,7 +11,7 @@ UM.PointingRectangle
id: base
property real sourceWidth: 0
width: UM.Theme.getSize("tooltip").width
height: textScroll.height + UM.Theme.getSize("tooltip_margins").height * 2
height: UM.Theme.getSize("tooltip").height
color: UM.Theme.getColor("tooltip")
arrowSize: UM.Theme.getSize("default_arrow").width
@ -81,12 +81,11 @@ UM.PointingRectangle
ScrollView
{
id: textScroll
width: parent.width
height: Math.min(label.height, base.parent.height)
width: base.width
height: base.height
ScrollBar.horizontal: ScrollBar {
active: false //Only allow vertical scrolling. We should grow vertically only, but due to how the label is positioned it allocates space in the ScrollView horizontally.
}
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
UM.Label
{

View file

@ -195,7 +195,7 @@ Item
onPositionChanged: {
// This removes focus from items when scrolling.
// This fixes comboboxes staying open and scrolling container
if (!activeFocus) {
if (!activeFocus && !filter.activeFocus) {
forceActiveFocus();
}
}
@ -227,9 +227,7 @@ Item
id: delegate
width: contents.width - (scrollBar.width + UM.Theme.getSize("narrow_margin").width)
Behavior on height { NumberAnimation { duration: 100 } }
opacity: enabled ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }
enabled: provider.properties.enabled === "True"
property var definition: model
@ -351,10 +349,7 @@ Item
function onFocusReceived()
{
contents.indexWithFocus = index
animateContentY.from = contents.contentY
contents.positionViewAtIndex(index, ListView.Contain)
animateContentY.to = contents.contentY
animateContentY.running = true
}
function onSetActiveFocusToNextSetting(forward)
{
@ -386,35 +381,6 @@ Item
}
}
NumberAnimation {
id: animateContentY
target: contents
property: "contentY"
duration: 50
}
add: Transition {
SequentialAnimation {
NumberAnimation { properties: "height"; from: 0; duration: 100 }
NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
}
}
remove: Transition {
SequentialAnimation {
NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
NumberAnimation { properties: "height"; to: 0; duration: 100 }
}
}
addDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 100 }
}
removeDisplaced: Transition {
SequentialAnimation {
PauseAnimation { duration: 100; }
NumberAnimation { properties: "x,y"; duration: 100 }
}
}
Cura.Menu
{
id: contextMenu

View file

@ -1,3 +1,70 @@
[5.0]
Beta
<i><a href="https://youtu.be/kRj7pR4OkQA">Watch the launch event</a> to learn more about Ultimaker Cura 5.0 beta.</i>
* New slicing engine
Following special beta releases to test the Arachne engine, we are pleased to announce our new slicing engine is here in Ultimaker Cura! This all-new engine uses variable line widths when preparing files for printing, meaning you can now print thin and intricate parts more accurately and with greater strength.
* Renewed the Ultimaker Cura Marketplace
We have streamlined the workflow for accessing the Ultimaker Marketplace inside of Ultimaker Cura. The UI has been improved and its now easier and faster to find and install plugins and material profiles.
* Improved print profiles for Ultimaker printers
The new slicing engine in Ultimaker Cura 5.0 beta has helped us to improve our print profiles. This means that users of Ultimaker printers can achieve speed increases of up to 20%.
* Upgrade from Qt5 to Qt6
We now support Apple M1 chips
* Other new features and improvements:
- New Cura icon
- New Cura splash screen
- Updated the digital build plates for Ultimaker printers
- Introduce Minimum Wall Line Width, contributed by BagelOrb
- Settings for metal printing implemented
- Shrinkage compensation is now available for PLA, tPLA and PETG
- Improved default Line Widths for Spiralize
- Decrease resolution to remove some buffer underruns
- Removed the setting Center Last from Wall Ordering
- Incomplete languages are now shown in the language drop-down menu
* Bug fixes:
- Added the Scale Fan Speed From 0 to 1 setting for printers that interpreted fan speed as percentages
- Fixed a bug with extra travel moves increased the printing time, contributed by BagelOrb
- Fixed a bug where Monotonic Ironing breaks Ironing, contributed by BagelOrb
- Changed the priority of CuraEngine
- Fixed a bug where increasing Filter distances creates extremely wide lines, contributed by BagelOrb
- Fixed double scroll bar, contributed by fieldOfView
- Fixed a bug where maximum resolution/deviation was not applied to surface mode, contributed by BagelOrb
- Fixed a bug where the seam placement was uneven
- Fixed a bug where Top Surface Skin Layers didn't work
- Fixed a bug where Speed in the flow setting were not respected
- Fixed a bug with unnecessary retracted travel moves
- Fixed a bug where the Ironing Inset didn't work
- Fixed a bug where Support Layers were missing
- Improved the visibility of the checkboxes
- Fixed a crash if Randomize Infill Start was used
- Fixed a bug where Combing was in the wrong part with dual extrusion
- Fixed a crash with Bridging and Top Surface Skin Layers
- Fixed a bug where modifier meshes didn't work in one-at-a-time mode
- Fixed a bug where Tree Support Branches where not being generated
- Fixed a bug where less support was generated
- Changed the possibility for 100% Infill Bottom Layer for Spiralize, contributed by BagelOrb
- Fixed disallowed areas for Brim gap, contributed by BagelOrb
* Printer definitions, profiles and materials:
- Added Atom 3 and Atom 3 Lite printer definitions, contributed by Daniel-Kurth
- Added Layer One Black PLA, Dark Grey PLA and White PLA, contributed by Daniel-Kurth
- Added FLSUN Q5 printer definition, contributed by kreuzhofer
- Added Creatlity CR100 printer definition, contributed by bodyboarder2528
- Added Mixware Hyper-S printer definition, contributed by mixware011
- Added Creality Sermoon D1 printer definition, contributed by felixstif
- Added Volumic SH65, Stream30Pro MK3 and Stream30Ultra SC2 printer definitions, contributed by VOLUMIC
- Updated Eryone Thinker and ER20 profiles, contributed by Eryone
- Updated Atom 2 profile, contributed by lin-ycv
- Added Hellbot Hidra and Magna series printer definitions, contributed by DevelopmentHellbot
- Updated Snapmaker 2 End-Gcode, contributed by Rolzad73
- Updated the Tinyboy Fabricator printer definitions, contributed by reibuehl
- Updated the Creality Ender 5 printer profile, contributed by Rakhmanov
[4.13.1]
* Bug fixes
- Fixed a bug where tree support could go through the model