Merge branch 'master' of https://github.com/heed818/Cura
@ -10,9 +10,9 @@ For crashes and similar issues, please attach the following information:
|
||||
|
||||
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
|
||||
* The Cura GUI log file, located at
|
||||
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
|
||||
* `$USER/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
||||
* `$USER/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
|
||||
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
|
||||
* `$HOME/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
||||
* `$HOME/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
|
||||
|
||||
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
|
||||
|
||||
|
@ -109,7 +109,6 @@ class Account(QObject):
|
||||
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
|
||||
self._authorization_service.loadAuthDataFromPreferences()
|
||||
|
||||
|
||||
@pyqtProperty(int, notify=syncStateChanged)
|
||||
def syncState(self):
|
||||
return self._sync_state
|
||||
@ -178,6 +177,7 @@ class Account(QObject):
|
||||
if error_message:
|
||||
if self._error_message:
|
||||
self._error_message.hide()
|
||||
Logger.log("w", "Failed to login: %s", error_message)
|
||||
self._error_message = Message(error_message, title = i18n_catalog.i18nc("@info:title", "Login failed"))
|
||||
self._error_message.show()
|
||||
self._logged_in = False
|
||||
|
@ -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.5.0"
|
||||
CuraSDKVersion = "7.6.0"
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraAppName # type: ignore
|
||||
|
@ -39,6 +39,7 @@ class ArrangeObjectsJob(Job):
|
||||
no_full_solution_message = Message(
|
||||
i18n_catalog.i18nc("@info:status",
|
||||
"Unable to find a location within the build volume for all objects"),
|
||||
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"))
|
||||
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"),
|
||||
message_type = Message.MessageType.ERROR)
|
||||
no_full_solution_message.show()
|
||||
self.finished.emit(self)
|
||||
|
@ -7,7 +7,7 @@ import re
|
||||
import shutil
|
||||
from copy import deepcopy
|
||||
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
|
||||
from typing import Dict, Optional, TYPE_CHECKING
|
||||
from typing import Dict, Optional, TYPE_CHECKING, List
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
@ -29,7 +29,7 @@ class Backup:
|
||||
IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
|
||||
"""These files should be ignored when making a backup."""
|
||||
|
||||
IGNORED_FOLDERS = [r"plugins"]
|
||||
IGNORED_FOLDERS = [] # type: List[str]
|
||||
|
||||
SECRETS_SETTINGS = ["general/ultimaker_auth_data"]
|
||||
"""Secret preferences that need to obfuscated when making a backup of Cura"""
|
||||
@ -166,6 +166,9 @@ class Backup:
|
||||
Logger.log("d", "Moving preferences file from %s to %s", backup_preferences_file, preferences_file)
|
||||
shutil.move(backup_preferences_file, preferences_file)
|
||||
|
||||
# Read the preferences from the newly restored configuration (or else the cached Preferences will override the restored ones)
|
||||
self._application.readPreferencesFromConfiguration()
|
||||
|
||||
# Restore the obfuscated settings
|
||||
self._illuminate(**secrets)
|
||||
|
||||
@ -190,11 +193,13 @@ class Backup:
|
||||
Logger.log("d", "Removing current data in location: %s", target_path)
|
||||
Resources.factoryReset()
|
||||
Logger.log("d", "Extracting backup to location: %s", target_path)
|
||||
try:
|
||||
archive.extractall(target_path)
|
||||
except (PermissionError, EnvironmentError):
|
||||
Logger.logException("e", "Unable to extract the backup due to permission or file system errors.")
|
||||
return False
|
||||
name_list = archive.namelist()
|
||||
for archive_filename in name_list:
|
||||
try:
|
||||
archive.extract(archive_filename, target_path)
|
||||
except (PermissionError, EnvironmentError):
|
||||
Logger.logException("e", f"Unable to extract the file {archive_filename} from the backup due to permission or file system errors.")
|
||||
CuraApplication.getInstance().processEvents()
|
||||
return True
|
||||
|
||||
def _obfuscate(self) -> Dict[str, str]:
|
||||
|
@ -54,17 +54,6 @@ class BackupsManager:
|
||||
backup = Backup(self._application, zip_file = zip_file, meta_data = meta_data)
|
||||
restored = backup.restore()
|
||||
|
||||
package_manager = self._application.getPackageManager()
|
||||
|
||||
# If the backup was made with Cura 4.10 (or higher), we no longer store plugins.
|
||||
# Since the restored backup doesn't have those plugins anymore, we should remove it from the list
|
||||
# of installed plugins.
|
||||
if Version(meta_data.get("cura_release")) >= Version("4.10.0"):
|
||||
for package_id in package_manager.getAllInstalledPackageIDs():
|
||||
package_data = package_manager.getInstalledPackageInfo(package_id)
|
||||
if package_data.get("package_type") == "plugin" and not package_data.get("is_bundled"):
|
||||
package_manager.removePackage(package_id)
|
||||
|
||||
if restored:
|
||||
# At this point, Cura will need to restart for the changes to take effect.
|
||||
# We don't want to store the data at this point as that would override the just-restored backup.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import numpy
|
||||
@ -95,9 +95,11 @@ class BuildVolume(SceneNode):
|
||||
self._edge_disallowed_size = None
|
||||
|
||||
self._build_volume_message = Message(catalog.i18nc("@info:status",
|
||||
"The build volume height has been reduced due to the value of the"
|
||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
|
||||
"The build volume height has been reduced due to the value of the"
|
||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||
" with printed models."),
|
||||
title = catalog.i18nc("@info:title", "Build Volume"),
|
||||
message_type = Message.MessageType.WARNING)
|
||||
|
||||
self._global_container_stack = None # type: Optional[GlobalStack]
|
||||
|
||||
@ -916,6 +918,8 @@ class BuildVolume(SceneNode):
|
||||
return {}
|
||||
|
||||
for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"):
|
||||
if len(area) == 0:
|
||||
continue # Numpy doesn't deal well with 0-length arrays, since it can't determine the dimensionality of them.
|
||||
polygon = Polygon(numpy.array(area, numpy.float32))
|
||||
polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
|
||||
machine_disallowed_polygons.append(polygon)
|
||||
|
@ -708,6 +708,8 @@ class CuraApplication(QtApplication):
|
||||
@pyqtSlot(str)
|
||||
def discardOrKeepProfileChangesClosed(self, option: str) -> None:
|
||||
global_stack = self.getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return
|
||||
if option == "discard":
|
||||
for extruder in global_stack.extruderList:
|
||||
extruder.userChanges.clear()
|
||||
|
@ -53,6 +53,9 @@ class ExtrudersModel(ListModel):
|
||||
EnabledRole = Qt.UserRole + 11
|
||||
"""Is the extruder enabled?"""
|
||||
|
||||
MaterialTypeRole = Qt.UserRole + 12
|
||||
"""The type of the material (e.g. PLA, ABS, PETG, etc.)."""
|
||||
|
||||
defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
|
||||
"""List of colours to display if there is no material or the material has no known colour. """
|
||||
|
||||
@ -75,6 +78,7 @@ class ExtrudersModel(ListModel):
|
||||
self.addRoleName(self.StackRole, "stack")
|
||||
self.addRoleName(self.MaterialBrandRole, "material_brand")
|
||||
self.addRoleName(self.ColorNameRole, "color_name")
|
||||
self.addRoleName(self.MaterialTypeRole, "material_type")
|
||||
self._update_extruder_timer = QTimer()
|
||||
self._update_extruder_timer.setInterval(100)
|
||||
self._update_extruder_timer.setSingleShot(True)
|
||||
@ -193,7 +197,8 @@ class ExtrudersModel(ListModel):
|
||||
"variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core
|
||||
"stack": extruder,
|
||||
"material_brand": material_brand,
|
||||
"color_name": color_name
|
||||
"color_name": color_name,
|
||||
"material_type": extruder.material.getMetaDataEntry("material") if extruder.material else "",
|
||||
}
|
||||
|
||||
items.append(item)
|
||||
@ -210,7 +215,7 @@ class ExtrudersModel(ListModel):
|
||||
"id": "",
|
||||
"name": catalog.i18nc("@menuitem", "Not overridden"),
|
||||
"enabled": True,
|
||||
"color": "#ffffff",
|
||||
"color": "transparent",
|
||||
"index": -1,
|
||||
"definition": "",
|
||||
"material": "",
|
||||
@ -218,6 +223,7 @@ class ExtrudersModel(ListModel):
|
||||
"stack": None,
|
||||
"material_brand": "",
|
||||
"color_name": "",
|
||||
"material_type": "",
|
||||
}
|
||||
items.append(item)
|
||||
if self._items != items:
|
||||
|
@ -21,16 +21,6 @@ if TYPE_CHECKING:
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
class MaterialManagementModel(QObject):
|
||||
"""Proxy class to the materials page in the preferences.
|
||||
|
||||
This class handles the actions in that page, such as creating new materials, renaming them, etc.
|
||||
"""
|
||||
def __init__(self, parent: QObject) -> None:
|
||||
super().__init__(parent)
|
||||
cura_application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
self._preferred_export_all_path = None # type: Optional[QUrl] # Path to export all materials to. None if not yet initialised.
|
||||
cura_application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
|
||||
favoritesChanged = pyqtSignal(str)
|
||||
"""Triggered when a favorite is added or removed.
|
||||
|
||||
@ -271,25 +261,8 @@ class MaterialManagementModel(QObject):
|
||||
except ValueError: # Material was not in the favorites list.
|
||||
Logger.log("w", "Material {material_base_file} was already not a favorite material.".format(material_base_file = material_base_file))
|
||||
|
||||
def _onOutputDevicesChanged(self) -> None:
|
||||
"""
|
||||
When the list of output devices changes, we may want to update the
|
||||
preferred export path.
|
||||
"""
|
||||
cura_application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
device_manager = cura_application.getOutputDeviceManager()
|
||||
devices = device_manager.getOutputDevices()
|
||||
for device in devices:
|
||||
if device.__class__.__name__ == "RemovableDriveOutputDevice":
|
||||
self._preferred_export_all_path = QUrl.fromLocalFile(device.getId())
|
||||
break
|
||||
else: # No removable drives? Use local path.
|
||||
self._preferred_export_all_path = cura_application.getDefaultPath("dialog_material_path")
|
||||
self.outputDevicesChanged.emit()
|
||||
|
||||
outputDevicesChanged = pyqtSignal() # Triggered when adding or removing removable drives.
|
||||
@pyqtProperty(QUrl, notify = outputDevicesChanged)
|
||||
def preferredExportAllPath(self) -> QUrl:
|
||||
@pyqtSlot(result = QUrl)
|
||||
def getPreferredExportAllPath(self) -> QUrl:
|
||||
"""
|
||||
Get the preferred path to export materials to.
|
||||
|
||||
@ -297,9 +270,14 @@ class MaterialManagementModel(QObject):
|
||||
file path.
|
||||
:return: The preferred path to export all materials to.
|
||||
"""
|
||||
if self._preferred_export_all_path is None: # Not initialised yet. Can happen when output devices changed before class got created.
|
||||
self._onOutputDevicesChanged()
|
||||
return self._preferred_export_all_path
|
||||
cura_application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
device_manager = cura_application.getOutputDeviceManager()
|
||||
devices = device_manager.getOutputDevices()
|
||||
for device in devices:
|
||||
if device.__class__.__name__ == "RemovableDriveOutputDevice":
|
||||
return QUrl.fromLocalFile(device.getId())
|
||||
else: # No removable drives? Use local path.
|
||||
return cura_application.getDefaultPath("dialog_material_path")
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def exportAll(self, file_path: QUrl) -> None:
|
||||
|
@ -114,6 +114,7 @@ class QualitySettingsModel(ListModel):
|
||||
global_container = None if len(global_containers) == 0 else global_containers[0]
|
||||
extruders_containers = {pos: container_registry.findContainers(id = quality_changes_group.metadata_per_extruder[pos]["id"]) for pos in quality_changes_group.metadata_per_extruder}
|
||||
extruders_container = {pos: None if not containers else containers[0] for pos, containers in extruders_containers.items()}
|
||||
quality_changes_metadata = None
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION and global_container:
|
||||
quality_changes_metadata = global_container.getMetaData()
|
||||
else:
|
||||
|
@ -4,7 +4,7 @@ from typing import Type, TYPE_CHECKING, Optional, List
|
||||
|
||||
import keyring
|
||||
from keyring.backend import KeyringBackend
|
||||
from keyring.errors import NoKeyringError, PasswordSetError
|
||||
from keyring.errors import NoKeyringError, PasswordSetError, KeyringLocked
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
@ -39,6 +39,10 @@ class KeyringAttribute:
|
||||
self._store_secure = False
|
||||
Logger.logException("w", "No keyring backend present")
|
||||
return getattr(instance, self._name)
|
||||
except KeyringLocked:
|
||||
self._store_secure = False
|
||||
Logger.log("i", "Access to the keyring was denied.")
|
||||
return getattr(instance, self._name)
|
||||
else:
|
||||
return getattr(instance, self._name)
|
||||
|
||||
@ -48,7 +52,7 @@ class KeyringAttribute:
|
||||
if value is not None:
|
||||
try:
|
||||
keyring.set_password("cura", self._keyring_name, value)
|
||||
except PasswordSetError:
|
||||
except (PasswordSetError, KeyringLocked):
|
||||
self._store_secure = False
|
||||
if self._name not in DONT_EVER_STORE_LOCALLY:
|
||||
setattr(instance, self._name, value)
|
||||
|
@ -54,6 +54,7 @@ class LocalAuthorizationServer:
|
||||
if self._web_server:
|
||||
# If the server is already running (because of a previously aborted auth flow), we don't have to start it.
|
||||
# We still inject the new verification code though.
|
||||
Logger.log("d", "Auth web server was already running. Updating the verification code")
|
||||
self._web_server.setVerificationCode(verification_code)
|
||||
return
|
||||
|
||||
@ -85,6 +86,7 @@ class LocalAuthorizationServer:
|
||||
except OSError:
|
||||
# OS error can happen if the socket was already closed. We really don't care about that case.
|
||||
pass
|
||||
Logger.log("d", "Local oauth2 web server was shut down")
|
||||
self._web_server = None
|
||||
self._web_server_thread = None
|
||||
|
||||
@ -96,12 +98,13 @@ class LocalAuthorizationServer:
|
||||
|
||||
:return: None
|
||||
"""
|
||||
Logger.log("d", "Local web server for authorization has started")
|
||||
if self._web_server:
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
self._web_server.serve_forever()
|
||||
except OSError as e:
|
||||
Logger.warning(str(e))
|
||||
except OSError:
|
||||
Logger.logException("w", "An exception happened while serving the auth server")
|
||||
else:
|
||||
# Leave the default behavior in non-windows platforms
|
||||
self._web_server.serve_forever()
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import collections
|
||||
@ -6,9 +6,11 @@ from typing import Optional, Dict, List, cast
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Resources import Resources
|
||||
from UM.Version import Version
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
#
|
||||
# This manager provides means to load texts to QML.
|
||||
@ -30,30 +32,33 @@ class TextManager(QObject):
|
||||
# Load change log texts and organize them with a dict
|
||||
try:
|
||||
file_path = Resources.getPath(Resources.Texts, "change_log.txt")
|
||||
except FileNotFoundError:
|
||||
except FileNotFoundError as e:
|
||||
# I have no idea how / when this happens, but we're getting crash reports about it.
|
||||
return ""
|
||||
return catalog.i18nc("@text:window", "The release notes could not be opened.") + "<br>" + str(e)
|
||||
change_logs_dict = {} # type: Dict[Version, Dict[str, List[str]]]
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
open_version = None # type: Optional[Version]
|
||||
open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog
|
||||
for line in f:
|
||||
line = line.replace("\n", "")
|
||||
if "[" in line and "]" in line:
|
||||
line = line.replace("[", "")
|
||||
line = line.replace("]", "")
|
||||
open_version = Version(line)
|
||||
if open_version > Version([14, 99, 99]): # Bit of a hack: We released the 15.x.x versions before 2.x
|
||||
open_version = Version([0, open_version.getMinor(), open_version.getRevision(), open_version.getPostfixVersion()])
|
||||
open_header = ""
|
||||
change_logs_dict[open_version] = collections.OrderedDict()
|
||||
elif line.startswith("*"):
|
||||
open_header = line.replace("*", "")
|
||||
change_logs_dict[cast(Version, open_version)][open_header] = []
|
||||
elif line != "":
|
||||
if open_header not in change_logs_dict[cast(Version, open_version)]:
|
||||
try:
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
open_version = None # type: Optional[Version]
|
||||
open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog
|
||||
for line in f:
|
||||
line = line.replace("\n", "")
|
||||
if "[" in line and "]" in line:
|
||||
line = line.replace("[", "")
|
||||
line = line.replace("]", "")
|
||||
open_version = Version(line)
|
||||
if open_version > Version([14, 99, 99]): # Bit of a hack: We released the 15.x.x versions before 2.x
|
||||
open_version = Version([0, open_version.getMinor(), open_version.getRevision(), open_version.getPostfixVersion()])
|
||||
open_header = ""
|
||||
change_logs_dict[open_version] = collections.OrderedDict()
|
||||
elif line.startswith("*"):
|
||||
open_header = line.replace("*", "")
|
||||
change_logs_dict[cast(Version, open_version)][open_header] = []
|
||||
change_logs_dict[cast(Version, open_version)][open_header].append(line)
|
||||
elif line != "":
|
||||
if open_header not in change_logs_dict[cast(Version, open_version)]:
|
||||
change_logs_dict[cast(Version, open_version)][open_header] = []
|
||||
change_logs_dict[cast(Version, open_version)][open_header].append(line)
|
||||
except EnvironmentError as e:
|
||||
return catalog.i18nc("@text:window", "The release notes could not be opened.") + "<br>" + str(e)
|
||||
|
||||
# Format changelog text
|
||||
content = ""
|
||||
|
@ -29,7 +29,7 @@ class WhatsNewPagesModel(WelcomePagesModel):
|
||||
for filename in files:
|
||||
basename = os.path.basename(filename)
|
||||
base, ext = os.path.splitext(basename)
|
||||
if ext not in include or not base.isdigit():
|
||||
if ext.lower() not in include or not base.isdigit():
|
||||
continue
|
||||
page_no = int(base)
|
||||
highest = max(highest, page_no)
|
||||
|
@ -419,7 +419,7 @@ UM.Dialog
|
||||
width: warningLabel.height
|
||||
height: width
|
||||
|
||||
source: UM.Theme.getIcon("notice")
|
||||
source: UM.Theme.getIcon("Information")
|
||||
color: palette.text
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for reading 3MF files.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for writing 3MF files.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,5 +3,5 @@
|
||||
"author": "fieldOfView",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading AMF files.",
|
||||
"api": "7.5.0"
|
||||
"api": 7
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Backup and restore your configuration.",
|
||||
"version": "1.2.0",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import threading
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import sentry_sdk
|
||||
from PyQt5.QtNetwork import QNetworkReply
|
||||
|
||||
from UM.Job import Job
|
||||
@ -99,13 +98,7 @@ class CreateBackupJob(Job):
|
||||
if HttpRequestManager.safeHttpStatus(reply) == 400:
|
||||
errors = json.loads(replyText)["errors"]
|
||||
if "moreThanMaximum" in [error["code"] for error in errors if error["meta"] and error["meta"]["field_name"] == "backup_size"]:
|
||||
if self._backup_zip is None: # will never happen; keep mypy happy
|
||||
zip_error = "backup is None."
|
||||
else:
|
||||
zip_error = "{} exceeds max size.".format(str(len(self._backup_zip)))
|
||||
sentry_sdk.capture_message("backup failed: {}".format(zip_error), level ="warning")
|
||||
self.backup_upload_error_message = catalog.i18nc("@error:file_size", "The backup exceeds the maximum file size.")
|
||||
from sentry_sdk import capture_message
|
||||
|
||||
self._job_done.set()
|
||||
return
|
||||
|
@ -93,7 +93,7 @@ class DriveApiService:
|
||||
def _onRestoreFinished(self, job: "RestoreBackupJob") -> None:
|
||||
if job.restore_backup_error_message != "":
|
||||
# If the job contains an error message we pass it along so the UI can display it.
|
||||
self.restoringStateChanged.emit(is_restoring=False)
|
||||
self.restoringStateChanged.emit(is_restoring = False)
|
||||
else:
|
||||
self.restoringStateChanged.emit(is_restoring = False, error_message = job.restore_backup_error_message)
|
||||
|
||||
|
@ -34,6 +34,9 @@ class DrivePluginExtension(QObject, Extension):
|
||||
# Signal emitted when preferences changed (like auto-backup).
|
||||
preferencesChanged = pyqtSignal()
|
||||
|
||||
# Signal emitted when the id of the backup-to-be-restored is changed
|
||||
backupIdBeingRestoredChanged = pyqtSignal(arguments = ["backup_id_being_restored"])
|
||||
|
||||
DATE_FORMAT = "%d/%m/%Y %H:%M:%S"
|
||||
|
||||
def __init__(self) -> None:
|
||||
@ -45,6 +48,7 @@ class DrivePluginExtension(QObject, Extension):
|
||||
self._backups = [] # type: List[Dict[str, Any]]
|
||||
self._is_restoring_backup = False
|
||||
self._is_creating_backup = False
|
||||
self._backup_id_being_restored = ""
|
||||
|
||||
# Initialize services.
|
||||
preferences = CuraApplication.getInstance().getPreferences()
|
||||
@ -52,6 +56,7 @@ class DrivePluginExtension(QObject, Extension):
|
||||
|
||||
# Attach signals.
|
||||
CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
CuraApplication.getInstance().applicationShuttingDown.connect(self._onApplicationShuttingDown)
|
||||
self._drive_api_service.restoringStateChanged.connect(self._onRestoringStateChanged)
|
||||
self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged)
|
||||
|
||||
@ -75,6 +80,10 @@ class DrivePluginExtension(QObject, Extension):
|
||||
if self._drive_window:
|
||||
self._drive_window.show()
|
||||
|
||||
def _onApplicationShuttingDown(self):
|
||||
if self._drive_window:
|
||||
self._drive_window.hide()
|
||||
|
||||
def _autoBackup(self) -> None:
|
||||
preferences = CuraApplication.getInstance().getPreferences()
|
||||
if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo():
|
||||
@ -100,10 +109,11 @@ class DrivePluginExtension(QObject, Extension):
|
||||
if logged_in:
|
||||
self.refreshBackups()
|
||||
|
||||
def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: str = None) -> None:
|
||||
def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: Optional[str] = None) -> None:
|
||||
self._is_restoring_backup = is_restoring
|
||||
self.restoringStateChanged.emit()
|
||||
if error_message:
|
||||
self.backupIdBeingRestored = ""
|
||||
Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show()
|
||||
|
||||
def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None:
|
||||
@ -152,6 +162,7 @@ class DrivePluginExtension(QObject, Extension):
|
||||
for backup in self._backups:
|
||||
if backup.get("backup_id") == backup_id:
|
||||
self._drive_api_service.restoreBackup(backup)
|
||||
self.setBackupIdBeingRestored(backup_id)
|
||||
return
|
||||
Logger.log("w", "Unable to find backup with the ID %s", backup_id)
|
||||
|
||||
@ -166,3 +177,12 @@ class DrivePluginExtension(QObject, Extension):
|
||||
def _backupDeletedCallback(self, success: bool):
|
||||
if success:
|
||||
self.refreshBackups()
|
||||
|
||||
def setBackupIdBeingRestored(self, backup_id_being_restored: str) -> None:
|
||||
if backup_id_being_restored != self._backup_id_being_restored:
|
||||
self._backup_id_being_restored = backup_id_being_restored
|
||||
self.backupIdBeingRestoredChanged.emit()
|
||||
|
||||
@pyqtProperty(str, fset = setBackupIdBeingRestored, notify = backupIdBeingRestoredChanged)
|
||||
def backupIdBeingRestored(self) -> str:
|
||||
return self._backup_id_being_restored
|
||||
|
@ -20,7 +20,7 @@ RowLayout
|
||||
{
|
||||
id: infoButton
|
||||
text: catalog.i18nc("@button", "Want more?")
|
||||
iconSource: UM.Theme.getIcon("info")
|
||||
iconSource: UM.Theme.getIcon("Information")
|
||||
onClicked: Qt.openUrlExternally("https://goo.gl/forms/QACEP8pP3RV60QYG2")
|
||||
visible: backupListFooter.showInfoButton
|
||||
}
|
||||
@ -29,7 +29,7 @@ RowLayout
|
||||
{
|
||||
id: createBackupButton
|
||||
text: catalog.i18nc("@button", "Backup Now")
|
||||
iconSource: UM.Theme.getIcon("plus")
|
||||
iconSource: UM.Theme.getIcon("Plus")
|
||||
enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup
|
||||
onClicked: CuraDrive.createBackup()
|
||||
busy: CuraDrive.isCreatingBackup
|
||||
|
@ -38,7 +38,7 @@ Item
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
color: UM.Theme.getColor("small_button_text")
|
||||
hoverColor: UM.Theme.getColor("small_button_text_hover")
|
||||
iconSource: UM.Theme.getIcon("info")
|
||||
iconSource: UM.Theme.getIcon("Information")
|
||||
onClicked: backupListItem.showDetails = !backupListItem.showDetails
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ Item
|
||||
text: catalog.i18nc("@button", "Restore")
|
||||
enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup
|
||||
onClicked: confirmRestoreDialog.visible = true
|
||||
busy: CuraDrive.backupIdBeingRestored == modelData.backup_id && CuraDrive.isRestoringBackup
|
||||
}
|
||||
|
||||
UM.SimpleButton
|
||||
@ -79,7 +80,7 @@ Item
|
||||
height: UM.Theme.getSize("message_close").height
|
||||
color: UM.Theme.getColor("small_button_text")
|
||||
hoverColor: UM.Theme.getColor("small_button_text_hover")
|
||||
iconSource: UM.Theme.getIcon("cross1")
|
||||
iconSource: UM.Theme.getIcon("Cancel")
|
||||
onClicked: confirmDeleteDialog.visible = true
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
@ -17,7 +17,7 @@ ColumnLayout
|
||||
// Cura version
|
||||
BackupListItemDetailsRow
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("application")
|
||||
iconSource: UM.Theme.getIcon("UltimakerCura")
|
||||
label: catalog.i18nc("@backuplist:label", "Cura Version")
|
||||
value: backupDetailsData.metadata.cura_release
|
||||
}
|
||||
@ -25,7 +25,7 @@ ColumnLayout
|
||||
// Machine count.
|
||||
BackupListItemDetailsRow
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("printer_single")
|
||||
iconSource: UM.Theme.getIcon("Printer")
|
||||
label: catalog.i18nc("@backuplist:label", "Machines")
|
||||
value: backupDetailsData.metadata.machine_count
|
||||
}
|
||||
@ -33,7 +33,7 @@ ColumnLayout
|
||||
// Material count
|
||||
BackupListItemDetailsRow
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("category_material")
|
||||
iconSource: UM.Theme.getIcon("Spool")
|
||||
label: catalog.i18nc("@backuplist:label", "Materials")
|
||||
value: backupDetailsData.metadata.material_count
|
||||
}
|
||||
@ -41,7 +41,7 @@ ColumnLayout
|
||||
// Profile count.
|
||||
BackupListItemDetailsRow
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("settings")
|
||||
iconSource: UM.Theme.getIcon("Sliders")
|
||||
label: catalog.i18nc("@backuplist:label", "Profiles")
|
||||
value: backupDetailsData.metadata.profile_count
|
||||
}
|
||||
@ -49,7 +49,7 @@ ColumnLayout
|
||||
// Plugin count.
|
||||
BackupListItemDetailsRow
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("plugin")
|
||||
iconSource: UM.Theme.getIcon("Plugin")
|
||||
label: catalog.i18nc("@backuplist:label", "Plugins")
|
||||
value: backupDetailsData.metadata.plugin_count
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||
from collections import defaultdict
|
||||
import os
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
|
||||
from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSlot
|
||||
import sys
|
||||
from time import time
|
||||
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QImage
|
||||
from PyQt5.QtGui import QDesktopServices, QImage
|
||||
|
||||
from UM.Backend.Backend import Backend, BackendState
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
@ -157,6 +157,18 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self.determineAutoSlicing()
|
||||
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._slicing_error_message = Message(
|
||||
text = catalog.i18nc("@message", "Slicing failed with an unexpected error. Please consider reporting a bug on our issue tracker."),
|
||||
title = catalog.i18nc("@message:title", "Slicing failed")
|
||||
)
|
||||
self._slicing_error_message.addAction(
|
||||
action_id = "report_bug",
|
||||
name = catalog.i18nc("@message:button", "Report a bug"),
|
||||
description = catalog.i18nc("@message:description", "Report a bug on Ultimaker Cura's issue tracker."),
|
||||
icon = "[no_icon]"
|
||||
)
|
||||
self._slicing_error_message.actionTriggered.connect(self._reportBackendError)
|
||||
|
||||
self._snapshot = None #type: Optional[QImage]
|
||||
|
||||
application.initializationFinished.connect(self.initialize)
|
||||
@ -922,9 +934,22 @@ class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
if not self._restart:
|
||||
if self._process: # type: ignore
|
||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) # type: ignore
|
||||
return_code = self._process.wait()
|
||||
if return_code != 0:
|
||||
Logger.log("e", f"Backend exited abnormally with return code {return_code}!")
|
||||
self._slicing_error_message.show()
|
||||
self.setState(BackendState.Error)
|
||||
self.stopSlicing()
|
||||
else:
|
||||
Logger.log("d", "Backend finished slicing. Resetting process and socket.")
|
||||
self._process = None # type: ignore
|
||||
|
||||
def _reportBackendError(self, _message_id: str, _action_id: str) -> None:
|
||||
"""
|
||||
Triggered when the user wants to report an error in the back-end.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl("https://github.com/Ultimaker/Cura/issues/new/choose"))
|
||||
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
"""Called when the global container stack changes"""
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "CuraEngine Backend",
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Provides the link to the CuraEngine slicing backend.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"version": "1.0.1",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for importing Cura profiles.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for exporting Cura profiles.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog":"cura"
|
||||
}
|
||||
|
@ -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.0.0",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 378.13 348.13" version="1.1">
|
||||
<defs
|
||||
id="defs7">
|
||||
<style
|
||||
id="style2">
|
||||
.cls-2,.cls-6{fill:#c5dbfb;}
|
||||
.cls-6,.cls-7{stroke-width:2px;}
|
||||
.cls-7{fill:#f3f8fe;}
|
||||
.cls-6,.cls-7{stroke:#061884;}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-2" d="M43,17V3H83a2,2,0,0,1,2,2V17Z" />
|
||||
<path fill="white" d="M 3 1 C 1.8954305 1 1 1.8954305 1 3 L 1 67 C 1 68.104569 1.8954305 69 3 69 L 72.152344 69 A 100 100 0 0 1 89 49.873047 L 89 19 C 89 17.895431 88.104569 17 87 17 L 56 17 L 40 1 L 3 1 z " />
|
||||
<path fill="#c5dbfb" d="M 3 0 C 1.3549904 0 0 1.3549904 0 3 L 0 67 C 0 68.64501 1.3549904 70 3 70 L 71.484375 70 A 100 100 0 0 1 72.835938 68 L 3 68 C 2.4358706 68 2 67.564129 2 67 L 2 3 C 2 2.4358706 2.4358706 2 3 2 L 39.585938 2 L 55.585938 18 L 87 18 C 87.564129 18 88 18.435871 88 19 L 88 50.765625 A 100 100 0 0 1 90 49.007812 L 90 19 C 90 17.35499 88.64501 16 87 16 L 56.414062 16 L 40.414062 0 L 3 0 z " />
|
||||
<path class="cls-2" d="M153,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||
<path fill="white" d="M 113 1 C 111.89543 1 111 1.8954305 111 3 L 111 35.201172 A 100 100 0 0 1 155 25 A 100 100 0 0 1 199 35.201172 L 199 19 C 199 17.895431 198.10457 17 197 17 L 166 17 L 150 1 L 113 1 z " />
|
||||
<path fill="#c5dbfb" d="M 113 0 C 111.35499 0 110 1.3549904 110 3 L 110 35.699219 A 100 100 0 0 1 112 34.716797 L 112 3 C 112 2.4358706 112.43587 2 113 2 L 149.58594 2 L 165.58594 18 L 197 18 C 197.56413 18 198 18.435871 198 19 L 198 34.716797 A 100 100 0 0 1 200 35.699219 L 200 19 C 200 17.35499 198.64501 16 197 16 L 166.41406 16 L 150.41406 0 L 113 0 z " />
|
||||
<path class="cls-2" d="M263,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||
<path fill="white" d="M 223 1 C 221.89543 1 221 1.8954305 221 3 L 221 49.875 A 100 100 0 0 1 237.84961 69 L 307 69 C 308.10457 69 309 68.104569 309 67 L 309 19 C 309 17.895431 308.10457 17 307 17 L 276 17 L 260 1 L 223 1 z " />
|
||||
<path fill="#c5dbfb" d="M 223 0 C 221.35499 0 220 1.3549904 220 3 L 220 49.005859 A 100 100 0 0 1 222 50.765625 L 222 3 C 222 2.4358706 222.43587 2 223 2 L 259.58594 2 L 275.58594 18 L 307 18 C 307.56413 18 308 18.435871 308 19 L 308 67 C 308 67.564129 307.56413 68 307 68 L 237.16406 68 A 100 100 0 0 1 238.51562 70 L 307 70 C 308.64501 70 310 68.64501 310 67 L 310 19 C 310 17.35499 308.64501 16 307 16 L 276.41406 16 L 260.41406 0 L 223 0 z " />
|
||||
<path fill="#c5dbfb" d="M 43 93 L 43 107 L 56.634766 107 A 100 100 0 0 1 60.259766 93 L 43 93 z " />
|
||||
<path fill="white" d="M 3 91 C 1.8954305 91 1 91.895431 1 93 L 1 157 C 1 158.10457 1.8954305 159 3 159 L 60.958984 159 A 100 100 0 0 1 55 125 A 100 100 0 0 1 56.634766 107 L 56 107 L 40 91 L 3 91 z " />
|
||||
<path fill="#c5dbfb" d="M 3 90 C 1.3549904 90 0 91.35499 0 93 L 0 157 C 0 158.64501 1.3549904 160 3 160 L 61.324219 160 A 100 100 0 0 1 60.603516 158 L 3 158 C 2.4358706 158 2 157.56413 2 157 L 2 93 C 2 92.435871 2.4358706 92 3 92 L 39.585938 92 L 55.585938 108 L 56.455078 108 A 100 100 0 0 1 56.822266 106 L 56.414062 106 L 40.414062 90 L 3 90 z " />
|
||||
<path class="cls-2" d="M263,107V93h40a2,2,0,0,1,2,2v12Z" />
|
||||
<path fill="white" d="M 249.04102 91 A 100 100 0 0 1 255 125 A 100 100 0 0 1 249.04102 159 L 307 159 C 308.10457 159 309 158.10457 309 157 L 309 109 C 309 107.89543 308.10457 107 307 107 L 276 107 L 260 91 L 249.04102 91 z " />
|
||||
<path fill="#c5dbfb" d="M 248.67578 90 A 100 100 0 0 1 249.39648 92 L 259.58594 92 L 275.58594 108 L 307 108 C 307.56413 108 308 108.43587 308 109 L 308 157 C 308 157.56413 307.56413 158 307 158 L 249.39844 158 A 100 100 0 0 1 248.67383 160 L 307 160 C 308.64501 160 310 158.64501 310 157 L 310 109 C 310 107.35499 308.64501 106 307 106 L 276.41406 106 L 260.41406 90 L 248.67578 90 z " />
|
||||
<path fill="#c5dbfb" d="M 43 183 L 43 197 L 85 197 L 85 196.41406 A 100 100 0 0 1 73.539062 183 L 43 183 z " />
|
||||
<path fill="white" d="M 3 181 C 1.8954305 181 1 181.89543 1 183 L 1 247 C 1 248.10457 1.8954305 249 3 249 L 87 249 C 88.104569 249 89 248.10457 89 247 L 89 200.125 A 100 100 0 0 1 85.603516 197 L 56 197 L 40 181 L 3 181 z " />
|
||||
<path fill="#c5dbfb" d="M 3 180 C 1.3549904 180 0 181.35499 0 183 L 0 247 C 0 248.64501 1.3549904 250 3 250 L 87 250 C 88.64501 250 90 248.64501 90 247 L 90 200.99414 A 100 100 0 0 1 88 199.23438 L 88 247 C 88 247.56413 87.564129 248 87 248 L 3 248 C 2.4358706 248 2 247.56413 2 247 L 2 183 C 2 182.43587 2.4358706 182 3 182 L 39.585938 182 L 55.585938 198 L 86.65625 198 A 100 100 0 0 1 84.580078 196 L 56.414062 196 L 40.414062 180 L 3 180 z " />
|
||||
<path fill="white" d="M 111 214.79883 L 111 247 C 111 248.10457 111.89543 249 113 249 L 197 249 C 198.10457 249 199 248.10457 199 247 L 199 214.79883 A 100 100 0 0 1 155 225 A 100 100 0 0 1 111 214.79883 z " />
|
||||
<path fill="#c5dbfb" d="M 110 214.30078 L 110 247 C 110 248.64501 111.35499 250 113 250 L 197 250 C 198.64501 250 200 248.64501 200 247 L 200 214.30078 A 100 100 0 0 1 198 215.2832 L 198 247 C 198 247.56413 197.56413 248 197 248 L 113 248 C 112.43587 248 112 247.56413 112 247 L 112 215.2832 A 100 100 0 0 1 110 214.30078 z " />
|
||||
<path class="cls-2" d="M263,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||
<path fill="white" d="M 237.84766 181 A 100 100 0 0 1 221 200.12695 L 221 247 C 221 248.10457 221.89543 249 223 249 L 307 249 C 308.10457 249 309 248.10457 309 247 L 309 199 C 309 197.89543 308.10457 197 307 197 L 276 197 L 260 181 L 237.84766 181 z " />
|
||||
<path fill="#c5dbfb" d="M 238.51562 180 A 100 100 0 0 1 237.16406 182 L 259.58594 182 L 275.58594 198 L 307 198 C 307.56413 198 308 198.43587 308 199 L 308 247 C 308 247.56413 307.56413 248 307 248 L 223 248 C 222.43587 248 222 247.56413 222 247 L 222 199.23438 A 100 100 0 0 1 220 200.99219 L 220 247 C 220 248.64501 221.35499 250 223 250 L 307 250 C 308.64501 250 310 248.64501 310 247 L 310 199 C 310 197.35499 308.64501 196 307 196 L 276.41406 196 L 260.41406 180 L 238.51562 180 z " />
|
||||
<path class="cls-6" d="M351.12,322.62h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7A10,10,0,0,1,351.12,322.62Z" transform="translate(850.61 309.91) rotate(135)" />
|
||||
<rect class="cls-7" x="293.75" y="225.25" width="40" height="117" transform="translate(-108.74 304.96) rotate(-45)" />
|
||||
<polyline class="cls-7" points="213.69 199.25 252.58 238.14 267.43 223.29 228.54 184.4" />
|
||||
<path fill="white" stroke="#061884" stroke-width="2px" d="M 154.94141 30 A 95 95 0 0 0 60 125 A 95 95 0 0 0 155 220 A 95 95 0 0 0 250 125 A 95 95 0 0 0 155 30 A 95 95 0 0 0 154.94141 30 z M 154.82812 40 A 85 85 0 0 1 155 40 A 85 85 0 0 1 240 125 A 85 85 0 0 1 155 210 A 85 85 0 0 1 70 125 A 85 85 0 0 1 154.82812 40 z " />
|
||||
<path class="cls-6" d="M256.37,227.87h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7a10,10,0,0,1,10-10Z" transform="translate(-89.12 257.58) rotate(-45)" />
|
||||
<path fill="white" d="M 154.94141 45 A 80 80 0 0 0 111 58.185547 L 111 67 C 111 68.104569 111.89543 69 113 69 L 197 69 C 198.10457 69 199 68.104569 199 67 L 199 58.1875 A 80 80 0 0 0 155 45 A 80 80 0 0 0 154.94141 45 z " />
|
||||
<path fill="#061884" d="M 112 57.539062 A 80 80 0 0 0 110 58.857422 L 110 67 C 110 68.64501 111.35499 70 113 70 L 197 70 C 198.64501 70 200 68.64501 200 67 L 200 58.857422 A 80 80 0 0 0 198 57.541016 L 198 67 C 198 67.564129 197.56413 68 197 68 L 113 68 C 112.43587 68 112 67.564129 112 67 L 112 57.539062 z " />
|
||||
<path fill="#196ef0" d="M 81.679688 93 A 80 80 0 0 0 77.050781 107 L 85 107 L 85 95 A 2 2 0 0 0 83 93 L 81.679688 93 z " />
|
||||
<path fill="white" d="M 77.050781 107 A 80 80 0 0 0 75 125 A 80 80 0 0 0 82.585938 159 L 87 159 C 88.104569 159 89 158.10457 89 157 L 89 109 C 89 107.89543 88.104569 107 87 107 L 77.050781 107 z " />
|
||||
<path fill="#061884" d="M 77.289062 106 A 80 80 0 0 0 76.828125 108 L 87 108 C 87.564129 108 88 108.43587 88 109 L 88 157 C 88 157.56413 87.564129 158 87 158 L 82.125 158 A 80 80 0 0 0 83.0625 160 L 87 160 C 88.64501 160 90 158.64501 90 157 L 90 109 C 90 107.35499 88.64501 106 87 106 L 77.289062 106 z " />
|
||||
<path fill="white" d="M 223 91 C 221.89543 91 221 91.895431 221 93 L 221 157 C 221 158.10457 221.89543 159 223 159 L 227.41406 159 A 80 80 0 0 0 235 125 A 80 80 0 0 0 227.41406 91 L 223 91 z " />
|
||||
<path fill="#061884" d="M 223 90 C 221.35499 90 220 91.35499 220 93 L 220 157 C 220 158.64501 221.35499 160 223 160 L 226.9375 160 A 80 80 0 0 0 227.87695 158 L 223 158 C 222.43587 158 222 157.56413 222 157 L 222 93 C 222 92.435871 222.43587 92 223 92 L 227.875 92 A 80 80 0 0 0 226.9375 90 L 223 90 z " />
|
||||
<path fill="#196ef0" d="M 153 183 L 153 197 L 189.86914 197 A 80 80 0 0 0 195 194.28125 L 195 185 A 2 2 0 0 0 193 183 L 153 183 z "/>
|
||||
<path fill="white" d="M 113 181 C 111.89543 181 111 181.89543 111 183 L 111 191.8125 A 80 80 0 0 0 155 205 A 80 80 0 0 0 189.86914 197 L 166 197 L 150 181 L 113 181 z " />
|
||||
<path fill="#061884" d="M 113 180 C 111.35499 180 110 181.35499 110 183 L 110 191.14258 A 80 80 0 0 0 112 192.45898 L 112 183 C 112 182.43587 112.43587 182 113 182 L 149.58594 182 L 165.58594 198 L 187.72461 198 A 80 80 0 0 0 191.86328 196 L 166.41406 196 L 150.41406 180 L 113 180 z " />
|
||||
<path fill="#061884" d="m 149.18,133.69 v -3.48 a 14.36,14.36 0 0 1 1.74,-7.25 20.17,20.17 0 0 1 6.4,-6.17 25.87,25.87 0 0 0 5.68,-4.79 7,7 0 0 0 1.48,-4.34 4.13,4.13 0 0 0 -1.93,-3.62 9,9 0 0 0 -5.14,-1.3 24.94,24.94 0 0 0 -7.34,1.16 45.2,45.2 0 0 0 -7.78,3.31 l -5.37,-10.64 a 48.41,48.41 0 0 1 9.89,-4.21 40.25,40.25 0 0 1 11.67,-1.61 q 9.57,0 14.9,4.43 a 14.16,14.16 0 0 1 5.32,11.41 15.41,15.41 0 0 1 -2.55,9 30.38,30.38 0 0 1 -7.92,7.34 32.11,32.11 0 0 0 -5.23,4.37 5.91,5.91 0 0 0 -1.34,4 v 2.41 z m -1.61,15.12 q 0,-4.38 2.46,-6.12 a 10,10 0 0 1 5.95,-1.75 9.69,9.69 0 0 1 5.77,1.75 q 2.46,1.74 2.46,6.12 0,4.22 -2.46,6 a 9.42,9.42 0 0 1 -5.77,1.84 9.69,9.69 0 0 1 -5.95,-1.84 q -2.46,-1.81 -2.46,-6 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 9.6 KiB |
@ -93,7 +93,7 @@ Popup
|
||||
}
|
||||
validator: RegExpValidator
|
||||
{
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,99}$/
|
||||
}
|
||||
|
||||
text: PrintInformation.jobName
|
||||
@ -148,7 +148,7 @@ Popup
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
text: "Create"
|
||||
enabled: newProjectNameTextField.text != "" && !busy
|
||||
enabled: newProjectNameTextField.text.length >= 2 && !busy
|
||||
|
||||
onClicked:
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ Item
|
||||
anchors.topMargin: UM.Theme.getSize("thin_margin").height
|
||||
validator: RegExpValidator
|
||||
{
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
||||
regExp: /^[\w\-\. ()]{0,255}$/
|
||||
}
|
||||
|
||||
text: PrintInformation.jobName
|
||||
@ -200,7 +200,7 @@ Item
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
text: "Save"
|
||||
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text != ""
|
||||
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1
|
||||
|
||||
onClicked:
|
||||
{
|
||||
|
@ -1,10 +1,12 @@
|
||||
// Copyright (C) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.6 as Cura
|
||||
@ -18,7 +20,7 @@ Item
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
property alias createNewProjectButtonVisible: createNewProjectButton.visible
|
||||
property bool createNewProjectButtonVisible: true
|
||||
|
||||
anchors
|
||||
{
|
||||
@ -29,31 +31,58 @@ Item
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
Label
|
||||
RowLayout
|
||||
{
|
||||
id: selectProjectLabel
|
||||
id: headerRow
|
||||
|
||||
text: "Select Project"
|
||||
font: UM.Theme.getFont("medium")
|
||||
color: UM.Theme.getColor("small_button_text")
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
visible: projectListContainer.visible
|
||||
}
|
||||
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: createNewProjectButton
|
||||
|
||||
anchors.verticalCenter: selectProjectLabel.verticalCenter
|
||||
anchors.right: parent.right
|
||||
text: "New Library project"
|
||||
|
||||
onClicked:
|
||||
anchors
|
||||
{
|
||||
createNewProjectPopup.open()
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Cura.TextField
|
||||
{
|
||||
id: searchBar
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: createNewProjectButton.height
|
||||
|
||||
onTextEdited: manager.projectFilter = text //Update the search filter when editing this text field.
|
||||
|
||||
leftIcon: UM.Theme.getIcon("Magnifier")
|
||||
placeholderText: "Search"
|
||||
}
|
||||
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: createNewProjectButton
|
||||
|
||||
text: "New Library project"
|
||||
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||
|
||||
onClicked:
|
||||
{
|
||||
createNewProjectPopup.open()
|
||||
}
|
||||
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
||||
}
|
||||
|
||||
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: upgradePlanButton
|
||||
|
||||
text: "Upgrade plan"
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||
tooltip: "You have reached the maximum number of projects allowed by your subscription. Please upgrade to the Professional subscription to create more projects."
|
||||
tooltipWidth: parent.width * 0.5
|
||||
|
||||
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-essentials/sign-up-cura?utm_source=cura&utm_medium=software&utm_campaign=lib-max")
|
||||
}
|
||||
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
||||
}
|
||||
|
||||
Item
|
||||
@ -76,19 +105,18 @@ Item
|
||||
{
|
||||
id: digitalFactoryImage
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: "../images/digital_factory.svg"
|
||||
source: searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: noLibraryProjectsLabel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "It appears that you don't have any projects in the Library yet."
|
||||
text: searchBar.text === "" ? "It appears that you don't have any projects in the Library yet." : "No projects found that match the search query."
|
||||
font: UM.Theme.getFont("medium")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
|
||||
Cura.TertiaryButton
|
||||
@ -97,6 +125,7 @@ Item
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Visit Digital Library"
|
||||
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
|
||||
visible: searchBar.text === "" //Show the link to Digital Library when there are no projects in the user's Library.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +135,7 @@ Item
|
||||
id: projectListContainer
|
||||
anchors
|
||||
{
|
||||
top: selectProjectLabel.bottom
|
||||
top: headerRow.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
|
@ -22,6 +22,7 @@ from .DFFileUploader import DFFileUploader
|
||||
from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
|
||||
from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
|
||||
from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
|
||||
from .DigitalFactoryFeatureBudgetResponse import DigitalFactoryFeatureBudgetResponse
|
||||
from .DigitalFactoryFileResponse import DigitalFactoryFileResponse
|
||||
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse
|
||||
from .PaginationLinks import PaginationLinks
|
||||
@ -54,9 +55,67 @@ class DigitalFactoryApiClient:
|
||||
self._http = HttpRequestManager.getInstance()
|
||||
self._on_error = on_error
|
||||
self._file_uploader = None # type: Optional[DFFileUploader]
|
||||
self._library_max_private_projects: Optional[int] = None
|
||||
|
||||
self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager]
|
||||
|
||||
def checkUserHasAccess(self, callback: Callable) -> None:
|
||||
"""Checks if the user has any sort of access to the digital library.
|
||||
A user is considered to have access if the max-# of private projects is greater then 0 (or -1 for unlimited).
|
||||
"""
|
||||
|
||||
def callbackWrap(response: Optional[Any] = None, *args, **kwargs) -> None:
|
||||
if (response is not None and isinstance(response, DigitalFactoryFeatureBudgetResponse) and
|
||||
response.library_max_private_projects is not None):
|
||||
callback(
|
||||
response.library_max_private_projects == -1 or # Note: -1 is unlimited
|
||||
response.library_max_private_projects > 0)
|
||||
self._library_max_private_projects = response.library_max_private_projects
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Response is not a feature budget, likely an error: {str(response)}")
|
||||
callback(False)
|
||||
|
||||
self._http.get(f"{self.CURA_API_ROOT}/feature_budgets",
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(callbackWrap, DigitalFactoryFeatureBudgetResponse, callbackWrap),
|
||||
error_callback = callbackWrap,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def checkUserCanCreateNewLibraryProject(self, callback: Callable) -> None:
|
||||
"""
|
||||
Checks if the user is allowed to create new library projects.
|
||||
A user is allowed to create new library projects if the haven't reached their maximum allowed private projects.
|
||||
"""
|
||||
|
||||
def callbackWrap(response: Optional[Any] = None, *args, **kwargs) -> None:
|
||||
if response is not None:
|
||||
if isinstance(response, DigitalFactoryProjectResponse): # The user has only one private project
|
||||
callback(True)
|
||||
elif isinstance(response, list) and all(isinstance(r, DigitalFactoryProjectResponse) for r in response):
|
||||
callback(len(response) < cast(int, self._library_max_private_projects))
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Incorrect response type received when requesting private projects: {str(response)}")
|
||||
callback(False)
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Response is empty, likely an error: {str(response)}")
|
||||
callback(False)
|
||||
|
||||
if self._library_max_private_projects is not None and self._library_max_private_projects > 0:
|
||||
# The user has a limit in the number of private projects they can create. Check whether they have already
|
||||
# reached that limit.
|
||||
# Note: Set the pagination manager to None when doing this get request, or else the next/previous links
|
||||
# of the pagination will become corrupted
|
||||
url = f"{self.CURA_API_ROOT}/projects?shared=false&limit={self._library_max_private_projects}"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(callbackWrap, DigitalFactoryProjectResponse, callbackWrap, pagination_manager = None),
|
||||
error_callback = callbackWrap,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
else:
|
||||
# If the limit is -1, then the user is allowed unlimited projects. If its 0 then they are not allowed to
|
||||
# create any projects
|
||||
callback(self._library_max_private_projects == -1)
|
||||
|
||||
def getProject(self, library_project_id: str, on_finished: Callable[[DigitalFactoryProjectResponse], Any], failed: Callable) -> None:
|
||||
"""
|
||||
Retrieves a digital factory project by its library project id.
|
||||
@ -73,7 +132,7 @@ class DigitalFactoryApiClient:
|
||||
error_callback = failed,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def getProjectsFirstPage(self, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
||||
def getProjectsFirstPage(self, search_filter: str, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
||||
"""
|
||||
Retrieves digital factory projects for the user that is currently logged in.
|
||||
|
||||
@ -81,13 +140,18 @@ class DigitalFactoryApiClient:
|
||||
according to the limit set in the pagination manager. If there is no projects pagination manager, this function
|
||||
leaves the project limit to the default set on the server side (999999).
|
||||
|
||||
:param search_filter: Text to filter the search results. If given an empty string, results are not filtered.
|
||||
:param on_finished: The function to be called after the result is parsed.
|
||||
:param failed: The function to be called if the request fails.
|
||||
"""
|
||||
url = "{}/projects".format(self.CURA_API_ROOT)
|
||||
url = f"{self.CURA_API_ROOT}/projects"
|
||||
query_character = "?"
|
||||
if self._projects_pagination_mgr:
|
||||
self._projects_pagination_mgr.reset() # reset to clear all the links and response metadata
|
||||
url += "?limit={}".format(self._projects_pagination_mgr.limit)
|
||||
url += f"{query_character}limit={self._projects_pagination_mgr.limit}"
|
||||
query_character = "&"
|
||||
if search_filter != "":
|
||||
url += f"{query_character}search={search_filter}"
|
||||
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
@ -301,12 +365,10 @@ class DigitalFactoryApiClient:
|
||||
:param on_finished: The function to be called after the result is parsed.
|
||||
:param on_error: The function to be called if anything goes wrong.
|
||||
"""
|
||||
|
||||
display_name = re.sub(r"[^a-zA-Z0-9- ./™®ö+']", " ", project_name)
|
||||
Logger.log("i", "Attempt to create new DF project '{}'.".format(display_name))
|
||||
Logger.log("i", "Attempt to create new DF project '{}'.".format(project_name))
|
||||
|
||||
url = "{}/projects".format(self.CURA_API_ROOT)
|
||||
data = json.dumps({"data": {"display_name": display_name}}).encode()
|
||||
data = json.dumps({"data": {"display_name": project_name}}).encode()
|
||||
self._http.put(url,
|
||||
scope = self._scope,
|
||||
data = data,
|
||||
|
@ -1,4 +1,6 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
@ -8,7 +10,7 @@ from enum import IntEnum
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any, cast
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QUrl
|
||||
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QTimer, QUrl
|
||||
from PyQt5.QtNetwork import QNetworkReply
|
||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
||||
|
||||
@ -89,6 +91,12 @@ class DigitalFactoryController(QObject):
|
||||
uploadFileError = Signal()
|
||||
uploadFileFinished = Signal()
|
||||
|
||||
"""Signal to inform about the state of user access."""
|
||||
userAccessStateChanged = pyqtSignal(bool)
|
||||
|
||||
"""Signal to inform whether the user is allowed to create more Library projects."""
|
||||
userCanCreateNewLibraryProjectChanged = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, application: CuraApplication) -> None:
|
||||
super().__init__(parent = None)
|
||||
|
||||
@ -106,12 +114,18 @@ class DigitalFactoryController(QObject):
|
||||
self._has_more_projects_to_load = False
|
||||
|
||||
self._account = self._application.getInstance().getCuraAPI().account # type: Account
|
||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||
|
||||
# Initialize the project model
|
||||
self._project_model = DigitalFactoryProjectModel()
|
||||
self._selected_project_idx = -1
|
||||
self._project_creation_error_text = "Something went wrong while creating a new project. Please try again."
|
||||
self._project_filter = ""
|
||||
self._project_filter_change_timer = QTimer()
|
||||
self._project_filter_change_timer.setInterval(200)
|
||||
self._project_filter_change_timer.setSingleShot(True)
|
||||
self._project_filter_change_timer.timeout.connect(self._applyProjectFilter)
|
||||
|
||||
# Initialize the file model
|
||||
self._file_model = DigitalFactoryFileModel()
|
||||
@ -131,6 +145,9 @@ class DigitalFactoryController(QObject):
|
||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
self._application.initializationFinished.connect(self._applicationInitializationFinished)
|
||||
|
||||
self._user_has_access = False
|
||||
self._user_account_can_create_new_project = False
|
||||
|
||||
def clear(self) -> None:
|
||||
self._project_model.clearProjects()
|
||||
self._api.clear()
|
||||
@ -143,16 +160,22 @@ class DigitalFactoryController(QObject):
|
||||
|
||||
self.setSelectedProjectIndex(-1)
|
||||
|
||||
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
||||
def callback(has_access, **kwargs):
|
||||
self._user_has_access = has_access
|
||||
self.userAccessStateChanged.emit(logged_in)
|
||||
|
||||
self._api.checkUserHasAccess(callback)
|
||||
|
||||
def userAccountHasLibraryAccess(self) -> bool:
|
||||
"""
|
||||
Checks whether the currently logged in user account has access to the Digital Library
|
||||
|
||||
:return: True if the user account has Digital Library access, else False
|
||||
"""
|
||||
subscriptions = [] # type: List[Dict[str, Any]]
|
||||
if self._account.userProfile:
|
||||
subscriptions = self._account.userProfile.get("subscriptions", [])
|
||||
return len(subscriptions) > 0
|
||||
if self._user_has_access:
|
||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||
return self._user_has_access
|
||||
|
||||
def initialize(self, preselected_project_id: Optional[str] = None) -> None:
|
||||
self.clear()
|
||||
@ -162,7 +185,7 @@ class DigitalFactoryController(QObject):
|
||||
if preselected_project_id:
|
||||
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
||||
else:
|
||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
|
||||
def setProjectAsPreselected(self, df_project: DigitalFactoryProjectResponse) -> None:
|
||||
"""
|
||||
@ -288,6 +311,38 @@ class DigitalFactoryController(QObject):
|
||||
self._selected_file_indices = file_indices
|
||||
self.selectedFileIndicesChanged.emit(file_indices)
|
||||
|
||||
def setProjectFilter(self, new_filter: str) -> None:
|
||||
"""
|
||||
Called when the user wants to change the search filter for projects.
|
||||
|
||||
The filter is not immediately applied. There is some delay to allow the user to finish typing.
|
||||
:param new_filter: The new filter that the user wants to apply.
|
||||
"""
|
||||
self._project_filter = new_filter
|
||||
self._project_filter_change_timer.start()
|
||||
|
||||
"""
|
||||
Signal to notify Qt that the applied filter has changed.
|
||||
"""
|
||||
projectFilterChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(str, notify = projectFilterChanged, fset = setProjectFilter)
|
||||
def projectFilter(self) -> str:
|
||||
"""
|
||||
The current search filter being applied to the project list.
|
||||
:return: The current search filter being applied to the project list.
|
||||
"""
|
||||
return self._project_filter
|
||||
|
||||
def _applyProjectFilter(self) -> None:
|
||||
"""
|
||||
Actually apply the current filter to search for projects with the user-defined search string.
|
||||
:return:
|
||||
"""
|
||||
self.clear()
|
||||
self.projectFilterChanged.emit()
|
||||
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
|
||||
@pyqtProperty(QObject, constant = True)
|
||||
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
||||
return self._project_model
|
||||
@ -502,7 +557,8 @@ class DigitalFactoryController(QObject):
|
||||
# false, we also need to clean it from the projects model
|
||||
self._project_model.clearProjects()
|
||||
self.setSelectedProjectIndex(-1)
|
||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
||||
self._has_preselected_project = new_has_preselected_project
|
||||
self.preselectedProjectChanged.emit()
|
||||
@ -511,6 +567,14 @@ class DigitalFactoryController(QObject):
|
||||
def hasPreselectedProject(self) -> bool:
|
||||
return self._has_preselected_project
|
||||
|
||||
def setCanCreateNewLibraryProject(self, can_create_new_library_project: bool) -> None:
|
||||
self._user_account_can_create_new_project = can_create_new_library_project
|
||||
self.userCanCreateNewLibraryProjectChanged.emit(self._user_account_can_create_new_project)
|
||||
|
||||
@pyqtProperty(bool, fset = setCanCreateNewLibraryProject, notify = userCanCreateNewLibraryProjectChanged)
|
||||
def userAccountCanCreateNewLibraryProject(self) -> bool:
|
||||
return self._user_account_can_create_new_project
|
||||
|
||||
@pyqtSlot(str, "QStringList")
|
||||
def saveFileToSelectedProject(self, filename: str, formats: List[str]) -> None:
|
||||
"""
|
||||
|
@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from .BaseModel import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class DigitalFactoryFeatureBudgetResponse(BaseModel):
|
||||
"""Class representing the capabilities of a user account for Digital Library.
|
||||
NOTE: For each max_..._projects fields, '-1' means unlimited!
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
library_can_use_business_value: Optional[bool] = False,
|
||||
library_can_use_comments: Optional[bool] = False,
|
||||
library_can_use_status: Optional[bool] = False,
|
||||
library_can_use_tags: Optional[bool] = False,
|
||||
library_can_use_technical_requirements: Optional[bool] = False,
|
||||
library_max_organization_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||
library_max_private_projects: Optional[int] = None, # -1 means unlimited
|
||||
library_max_team_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||
**kwargs) -> None:
|
||||
|
||||
self.library_can_use_business_value = library_can_use_business_value
|
||||
self.library_can_use_comments = library_can_use_comments
|
||||
self.library_can_use_status = library_can_use_status
|
||||
self.library_can_use_tags = library_can_use_tags
|
||||
self.library_can_use_technical_requirements = library_can_use_technical_requirements
|
||||
self.library_max_organization_shared_projects = library_max_organization_shared_projects # -1 means unlimited
|
||||
self.library_max_private_projects = library_max_private_projects # -1 means unlimited
|
||||
self.library_max_team_shared_projects = library_max_team_shared_projects # -1 means unlimited
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "max private: {}, max org: {}, max team: {}".format(
|
||||
self.library_max_private_projects,
|
||||
self.library_max_organization_shared_projects,
|
||||
self.library_max_team_shared_projects)
|
||||
|
||||
# Validates the model, raising an exception if the model is invalid.
|
||||
def validate(self) -> None:
|
||||
super().validate()
|
||||
# No validation for now, as the response can be "data: []", which should be interpreted as all False and 0's
|
@ -22,7 +22,7 @@ class DigitalFactoryFileProvider(FileProvider):
|
||||
self._dialog = None
|
||||
|
||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
self._controller.userAccessStateChanged.connect(self._onUserAccessStateChanged)
|
||||
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
||||
self.priority = 10
|
||||
|
||||
@ -53,7 +53,7 @@ class DigitalFactoryFileProvider(FileProvider):
|
||||
if not self._dialog:
|
||||
Logger.log("e", "Unable to create the Digital Library Open dialog.")
|
||||
|
||||
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
||||
def _onUserAccessStateChanged(self, logged_in: bool) -> None:
|
||||
"""
|
||||
Sets the enabled status of the DigitalFactoryFileProvider according to the account's login status
|
||||
:param logged_in: The new login status
|
||||
|
@ -45,7 +45,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
||||
self._writing = False
|
||||
|
||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
self._controller.userAccessStateChanged.connect(self._onUserAccessStateChanged)
|
||||
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
||||
|
||||
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||
@ -97,7 +97,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
||||
if not self._dialog:
|
||||
Logger.log("e", "Unable to create the Digital Library Save dialog.")
|
||||
|
||||
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
||||
def _onUserAccessStateChanged(self, logged_in: bool) -> None:
|
||||
"""
|
||||
Sets the enabled status of the DigitalFactoryOutputDevice according to the account's login status
|
||||
:param logged_in: The new login status
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
@ -37,7 +40,7 @@ def test_getProjectsFirstPage(api_client):
|
||||
failed_callback = MagicMock()
|
||||
|
||||
# Call
|
||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
||||
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||
|
||||
# Asserts
|
||||
pagination_manager.reset.assert_called_once() # Should be called since we asked for new set of projects
|
||||
@ -45,16 +48,16 @@ def test_getProjectsFirstPage(api_client):
|
||||
args = http_manager.get.call_args_list[0]
|
||||
|
||||
# Ensure that it's called with the right limit
|
||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20"
|
||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20&search=filter"
|
||||
|
||||
# Change the limit & try again
|
||||
http_manager.get.reset_mock()
|
||||
pagination_manager.limit = 80
|
||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
||||
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||
args = http_manager.get.call_args_list[0]
|
||||
|
||||
# Ensure that it's called with the right limit
|
||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80"
|
||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80&search=filter"
|
||||
|
||||
|
||||
def test_getMoreProjects_noNewProjects(api_client):
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Checks for firmware updates.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a machine actions for updating firmware.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Reads g-code from a compressed archive.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Writes g-code to a compressed archive.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for importing profiles from g-code files.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Victor Larchenko, Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Allows loading and displaying G-code files.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Writes g-code to a file.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -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.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for importing profiles from legacy Cura versions.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -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.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "Model Checker",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ Rectangle
|
||||
id: externalLinkIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: UM.Theme.getColor("text_link")
|
||||
source: UM.Theme.getIcon("external_link")
|
||||
source: UM.Theme.getIcon("LinkExternal")
|
||||
width: UM.Theme.getSize("monitor_external_link_icon").width
|
||||
height: UM.Theme.getSize("monitor_external_link_icon").height
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a monitor stage in Cura.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -24,7 +24,7 @@ Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: (label.height / 2) | 0
|
||||
width: height
|
||||
source: control.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right");
|
||||
source: control.checked ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleRight");
|
||||
color: control.hovered ? palette.highlight : palette.buttonText
|
||||
}
|
||||
UM.RecolorImage
|
||||
|
@ -73,38 +73,40 @@ class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHand
|
||||
|
||||
# Add all instances that are not added, but are in visibility list
|
||||
for item in visible:
|
||||
if settings.getInstance(item) is None: # Setting was not added already.
|
||||
definition = self._stack.getSettingDefinition(item)
|
||||
if definition:
|
||||
new_instance = SettingInstance(definition, settings)
|
||||
if settings.getInstance(item) is not None: # Setting was added already.
|
||||
continue
|
||||
definition = self._stack.getSettingDefinition(item)
|
||||
if not definition:
|
||||
Logger.log("w", f"Unable to add instance ({item}) to per-object visibility because we couldn't find the matching definition.")
|
||||
continue
|
||||
|
||||
new_instance = SettingInstance(definition, settings)
|
||||
stack_nr = -1
|
||||
stack = None
|
||||
# Check from what stack we should copy the raw property of the setting from.
|
||||
if self._stack.getProperty("machine_extruder_count", "value") > 1:
|
||||
if definition.limit_to_extruder != "-1":
|
||||
# A limit to extruder function was set and it's a multi extrusion machine. Check what stack we do need to use.
|
||||
stack_nr = str(int(round(float(self._stack.getProperty(item, "limit_to_extruder")))))
|
||||
|
||||
# Check if the found stack_number is in the extruder list of extruders.
|
||||
if stack_nr not in ExtruderManager.getInstance().extruderIds and self._stack.getProperty("extruder_nr", "value") is not None:
|
||||
stack_nr = -1
|
||||
stack = None
|
||||
# Check from what stack we should copy the raw property of the setting from.
|
||||
if self._stack.getProperty("machine_extruder_count", "value") > 1:
|
||||
if definition.limit_to_extruder != "-1":
|
||||
# A limit to extruder function was set and it's a multi extrusion machine. Check what stack we do need to use.
|
||||
stack_nr = str(int(round(float(self._stack.getProperty(item, "limit_to_extruder")))))
|
||||
|
||||
# Check if the found stack_number is in the extruder list of extruders.
|
||||
if stack_nr not in ExtruderManager.getInstance().extruderIds and self._stack.getProperty("extruder_nr", "value") is not None:
|
||||
stack_nr = -1
|
||||
# Use the found stack number to get the right stack to copy the value from.
|
||||
if stack_nr in ExtruderManager.getInstance().extruderIds:
|
||||
stack = ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0]
|
||||
else:
|
||||
stack = self._stack
|
||||
|
||||
# Use the found stack number to get the right stack to copy the value from.
|
||||
if stack_nr in ExtruderManager.getInstance().extruderIds:
|
||||
stack = ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0]
|
||||
else:
|
||||
stack = self._stack
|
||||
|
||||
# Use the raw property to set the value (so the inheritance doesn't break)
|
||||
if stack is not None:
|
||||
new_instance.setProperty("value", stack.getRawProperty(item, "value"))
|
||||
else:
|
||||
new_instance.setProperty("value", None)
|
||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||
settings.addInstance(new_instance)
|
||||
visibility_changed = True
|
||||
else:
|
||||
Logger.log("w", "Unable to add instance (%s) to per-object visibility because we couldn't find the matching definition", item)
|
||||
# Use the raw property to set the value (so the inheritance doesn't break)
|
||||
if stack is not None:
|
||||
new_instance.setProperty("value", stack.getRawProperty(item, "value"))
|
||||
else:
|
||||
new_instance.setProperty("value", None)
|
||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||
settings.addInstance(new_instance)
|
||||
visibility_changed = True
|
||||
|
||||
if visibility_changed:
|
||||
self.visibilityChanged.emit()
|
||||
|
@ -80,7 +80,7 @@ Item
|
||||
{
|
||||
id: normalButton
|
||||
text: catalog.i18nc("@label", "Normal model")
|
||||
iconSource: UM.Theme.getIcon("pos_normal");
|
||||
iconSource: UM.Theme.getIcon("Infill0");
|
||||
property bool needBorder: true
|
||||
checkable: true
|
||||
onClicked: setMeshType(normalMeshType);
|
||||
@ -92,7 +92,7 @@ Item
|
||||
{
|
||||
id: supportMeshButton
|
||||
text: catalog.i18nc("@label", "Print as support")
|
||||
iconSource: UM.Theme.getIcon("pos_print_as_support");
|
||||
iconSource: UM.Theme.getIcon("MeshTypeSupport");
|
||||
property bool needBorder: true
|
||||
checkable:true
|
||||
onClicked: setMeshType(supportMeshType)
|
||||
@ -104,7 +104,7 @@ Item
|
||||
{
|
||||
id: overlapMeshButton
|
||||
text: catalog.i18nc("@label", "Modify settings for overlaps")
|
||||
iconSource: UM.Theme.getIcon("pos_modify_overlaps");
|
||||
iconSource: UM.Theme.getIcon("MeshTypeIntersect");
|
||||
property bool needBorder: true
|
||||
checkable:true
|
||||
onClicked: setMeshType(infillMeshType)
|
||||
@ -116,7 +116,7 @@ Item
|
||||
{
|
||||
id: antiOverhangMeshButton
|
||||
text: catalog.i18nc("@label", "Don't support overlaps")
|
||||
iconSource: UM.Theme.getIcon("pos_modify_dont_support_overlap");
|
||||
iconSource: UM.Theme.getIcon("BlockSupportOverlaps");
|
||||
property bool needBorder: true
|
||||
checkable: true
|
||||
onClicked: setMeshType(antiOverhangMeshType)
|
||||
@ -306,7 +306,7 @@ Item
|
||||
height: width
|
||||
sourceSize.height: width
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("minus")
|
||||
source: UM.Theme.getIcon("Minus")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
@ -103,20 +103,27 @@ class PerObjectSettingsTool(Tool):
|
||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||
settings.addInstance(new_instance)
|
||||
|
||||
for property_key in ["top_bottom_thickness", "wall_thickness", "wall_line_count"]:
|
||||
# Override some settings to ensure that the infill mesh by default adds no skin or walls. Or remove them if not an infill mesh.
|
||||
specialized_settings = {
|
||||
"top_bottom_thickness": 0,
|
||||
"top_thickness": "=top_bottom_thickness",
|
||||
"bottom_thickness": "=top_bottom_thickness",
|
||||
"top_layers": "=0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))",
|
||||
"bottom_layers": "=0 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
|
||||
"wall_thickness": 0,
|
||||
"wall_line_count": "=max(1, round((wall_thickness - wall_line_width_0) / wall_line_width_x) + 1) if wall_thickness != 0 else 0"
|
||||
}
|
||||
for property_key in specialized_settings:
|
||||
if mesh_type == "infill_mesh":
|
||||
if settings.getInstance(property_key) is None:
|
||||
definition = stack.getSettingDefinition(property_key)
|
||||
new_instance = SettingInstance(definition, settings)
|
||||
# We just want the wall_line count to be there in case it was overriden in the global stack.
|
||||
# as such, we don't need to set a value.
|
||||
if property_key != "wall_line_count":
|
||||
new_instance.setProperty("value", 0)
|
||||
new_instance.setProperty("value", specialized_settings[property_key])
|
||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||
settings.addInstance(new_instance)
|
||||
settings_visibility_changed = True
|
||||
|
||||
elif old_mesh_type == "infill_mesh" and settings.getInstance(property_key) and (settings.getProperty(property_key, "value") == 0 or property_key == "wall_line_count"):
|
||||
elif old_mesh_type == "infill_mesh" and settings.getInstance(property_key) and property_key in specialized_settings:
|
||||
settings.removeInstance(property_key)
|
||||
settings_visibility_changed = True
|
||||
|
||||
|
@ -13,7 +13,7 @@ def getMetaData():
|
||||
"tool": {
|
||||
"name": i18n_catalog.i18nc("@label", "Per Model Settings"),
|
||||
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Model Settings"),
|
||||
"icon": "tool_icon.svg",
|
||||
"icon": "MeshType",
|
||||
"tool_panel": "PerObjectSettingsPanel.qml",
|
||||
"weight": 3
|
||||
},
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides the Per Model Settings.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="30px" height="30px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
||||
<title>per_model_settings</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M9.73076923,0 L9.226,1.345 L0.449,11 L0,11 L0.639,9.084 L8.896,0 L9.73076923,0 Z M8.49,3.472 L8.907,4.721 L3.199,11 L1.647,11 L8.49,3.472 Z M9.228,5.685 L9.645,6.935 L5.949,11 L4.397,11 L9.228,5.685 Z M9.966,7.899 L10.382,9.148 L8.699,11 L7.147,11 L9.966,7.899 Z M10.704,10.112 L11,11 L9.896,11 L10.704,10.112 Z M7.698,0 L1.332,7.004 L2.23,4.308 L6.146,0 L7.698,0 Z M4.948,0 L2.344,2.866 L1.89,1.656 L3.396,0 L4.948,0 Z M2.198,0 L1.54,0.724 L1.26923077,0 L2.198,0 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="per_model_settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Per-model" transform="translate(2.000000, 2.000000)">
|
||||
<polygon id="Path-Copy-5" fill="#000" points="1.26923077 0 9.73076923 0 8.46153846 3.38461538 11 11 0 11 2.53846154 3.38461538"></polygon>
|
||||
<polygon id="Path-Copy-8" fill="#000" points="14.2692308 13 22.7307692 13 21.4615385 16.3846154 24 24 13 24 15.5384615 16.3846154"></polygon>
|
||||
<g id="stripe" transform="translate(13.000000, 0.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Combined-Shape" fill="#000" xlink:href="#path-1"></use>
|
||||
</g>
|
||||
<path d="M1.990731,13.5 L3.06878027,16.374798 L0.693712943,23.5 L10.3062871,23.5 L7.93121973,16.374798 L9.009269,13.5 L1.990731,13.5 Z" id="Path-Copy-7" stroke="#000"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.8 KiB |
@ -154,7 +154,7 @@ UM.Dialog
|
||||
height: Math.round(control.height / 2.7)
|
||||
sourceSize.height: width
|
||||
color: palette.text
|
||||
source: UM.Theme.getIcon("cross1")
|
||||
source: UM.Theme.getIcon("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,7 +188,7 @@ UM.Dialog
|
||||
height: Math.round(control.height / 2.5)
|
||||
sourceSize.height: width
|
||||
color: control.enabled ? palette.text : disabledPalette.text
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
source: UM.Theme.getIcon("ChevronSingleDown")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,7 +222,7 @@ UM.Dialog
|
||||
height: Math.round(control.height / 2.5)
|
||||
sourceSize.height: width
|
||||
color: control.enabled ? palette.text : disabledPalette.text
|
||||
source: UM.Theme.getIcon("arrow_top")
|
||||
source: UM.Theme.getIcon("ChevronSingleUp")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -517,7 +517,7 @@ UM.Dialog
|
||||
}
|
||||
toolTipContentAlignment: Cura.ToolTip.ContentAlignment.AlignLeft
|
||||
onClicked: dialog.show()
|
||||
iconSource: "postprocessing.svg"
|
||||
iconSource: "Script.svg"
|
||||
fixedWidthMode: false
|
||||
}
|
||||
|
||||
@ -536,4 +536,4 @@ UM.Dialog
|
||||
labelText: activeScriptsList.count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
plugins/PostProcessingPlugin/Script.svg
Executable file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<polygon points="4.4,12 8.2,15.8 6.8,17.2 1.6,12 6.8,6.8 8.2,8.2" />
|
||||
<polygon points="22.4,12 17.2,17.2 15.8,15.8 19.6,12 15.8,8.2 17.2,6.8" />
|
||||
<rect x="3.9" y="11" transform="matrix(0.1236 -0.9923 0.9923 0.1236 -1.429 22.4317)" width="16.1" height="2" />
|
||||
</svg>
|
After Width: | Height: | Size: 380 B |
@ -2,7 +2,7 @@
|
||||
"name": "Post Processing",
|
||||
"author": "Ultimaker",
|
||||
"version": "2.2.1",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"description": "Extension that allows for user created scripts for post processing",
|
||||
"catalog": "cura"
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="512px"
|
||||
height="512px"
|
||||
viewBox="0 0 512 512"
|
||||
style="enable-background:new 0 0 512 512;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="postprocessing.svg"><metadata
|
||||
id="metadata9"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs7" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1104"
|
||||
inkscape:window-height="1006"
|
||||
id="namedview5"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.3359375"
|
||||
inkscape:cx="256"
|
||||
inkscape:cy="256"
|
||||
inkscape:window-x="701"
|
||||
inkscape:window-y="121"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" /><path
|
||||
d="M 402.15234 0 C 371.74552 4.7369516e-015 345.79114 10.752017 324.21875 32.324219 C 302.57788 53.89652 291.82617 79.851497 291.82617 110.18945 C 291.82617 127.34315 295.26662 143.09419 302.16602 157.44531 L 238.38477 221.20312 C 227.77569 210.95036 218.04331 201.50935 209.66016 193.32422 C 207.33386 190.99792 202.68042 189.48707 198.60938 191.92969 L 191.74609 196.11719 C 165.34252 169.24836 154.17609 158.42965 150.57031 145.40234 C 146.84822 131.79345 150.22148 113.64862 153.71094 106.90234 C 156.61882 101.55183 165.69233 96.550326 173.36914 95.96875 L 183.37109 106.20508 C 185.69739 108.53139 189.30456 108.53139 191.63086 106.20508 L 227.57227 69.681641 C 229.89858 67.355335 229.89858 63.517712 227.57227 61.191406 L 169.53125 2.21875 C 167.20494 -0.10755598 163.48147 -0.10755598 161.27148 2.21875 L 125.33008 38.742188 C 123.00378 41.068494 123.00378 44.906116 125.33008 47.232422 L 129.16992 51.1875 C 129.16992 56.88695 128.35573 65.727167 123.70312 70.496094 C 116.49157 77.823958 102.18413 69.332919 92.878906 75.962891 C 83.689998 82.476548 72.05746 92.944493 64.613281 100.38867 C 57.285417 107.83285 29.138171 137.37722 9.015625 187.16016 C -11.106922 236.94311 4.3632369 283.12 15.296875 295.2168 C 21.11264 301.61414 31.696982 308.12804 29.835938 296.03125 C 27.974892 283.81815 24.951448 241.47942 38.792969 224.14844 C 52.634489 206.81746 70.894726 192.62799 94.623047 191.46484 C 117.42084 190.30169 130.56529 198.09417 160.10938 228.10352 L 156.85156 234.15234 C 154.75788 238.10706 155.92175 243.10728 158.24805 245.43359 C 161.95717 248.74082 172.37305 258.96006 186.52539 273.04297 L 6.9511719 452.54883 C 2.2984329 457.14417 1.1842379e-015 462.71497 0 469.14844 C -1.1842379e-015 475.69681 2.2984329 481.15473 6.9511719 485.51953 L 26.308594 505.22266 C 31.018838 509.76054 36.589603 512 42.908203 512 C 49.341623 512 54.800053 509.76054 59.337891 505.22266 L 238.96875 325.6582 C 317.6609 404.95524 424.21289 513.40234 424.21289 513.40234 L 482.25391 454.43164 C 437.71428 411.9686 358.71135 336.76293 291.93164 272.71484 L 354.68945 209.98047 C 369.08663 216.91399 384.90203 220.37891 402.15234 220.37891 C 425.29988 220.37891 446.52947 213.53073 465.77344 199.83398 C 485.08493 186.1372 498.57775 168.33291 506.31641 146.34961 C 510.08303 135.39222 512 126.69334 512 120.25586 C 512 117.79044 511.24662 115.80572 509.87695 114.16211 C 508.50726 112.5185 506.59041 111.69531 504.125 111.69531 C 502.61835 111.69531 496.86414 114.5734 486.72852 120.39453 C 476.6614 126.21564 465.50054 132.85752 453.37891 140.32227 C 441.18878 147.78698 434.7515 151.75888 433.92969 152.23828 L 386.40234 125.94141 L 386.40234 70.8125 L 458.51562 29.242188 C 461.18649 27.461587 462.48633 25.202356 462.48633 22.394531 C 462.48633 19.586706 461.1865 17.325625 458.51562 15.476562 C 451.32484 10.545729 442.4896 6.780346 432.08008 4.0410156 C 421.60206 1.3701797 411.67159 0 402.15234 0 z "
|
||||
id="path3" /></svg>
|
Before Width: | Height: | Size: 4.4 KiB |
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from ..Script import Script
|
||||
@ -517,8 +517,13 @@ class PauseAtHeight(Script):
|
||||
|
||||
prepend_gcode += self.putValue(M = extrusion_mode_numeric) + " ; switch back to " + extrusion_mode_string + " E values\n"
|
||||
|
||||
# reset extrude value to pre pause value
|
||||
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
|
||||
# reset extrude value to pre pause value
|
||||
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
|
||||
|
||||
elif redo_layer:
|
||||
# All other options reset the E value to what it was before the pause because E things were added.
|
||||
# If it's not yet reset, it still needs to be reset if there were any redo layers.
|
||||
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
|
||||
|
||||
layer = prepend_gcode + layer
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.1 as Cura
|
||||
|
||||
import QtGraphicalEffects 1.0 // For the dropshadow
|
||||
|
||||
Item
|
||||
{
|
||||
id: prepareMenu
|
||||
|
||||
property var fileProviderModel: CuraApplication.getFileProviderModel()
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
@ -24,60 +25,44 @@ Item
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
leftMargin: UM.Theme.getSize("wide_margin").width * 2
|
||||
rightMargin: UM.Theme.getSize("wide_margin").width * 2
|
||||
}
|
||||
|
||||
// Item to ensure that all of the buttons are nicely centered.
|
||||
Item
|
||||
{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - 2 * UM.Theme.getSize("wide_margin").width
|
||||
height: parent.height
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout
|
||||
{
|
||||
id: itemRow
|
||||
|
||||
anchors.left: openFileButton.right
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width + openFileButton.width + openFileMenu.width
|
||||
property int machineSelectorWidth: Math.round((width - printSetupSelectorItem.width) / 3)
|
||||
|
||||
height: parent.height
|
||||
spacing: 0
|
||||
// This is a trick to make sure that the borders of the two adjacent buttons' borders overlap. Otherwise
|
||||
// there will be double border (one from each button)
|
||||
spacing: -UM.Theme.getSize("default_lining").width
|
||||
|
||||
Cura.MachineSelector
|
||||
{
|
||||
id: machineSelection
|
||||
headerCornerSide: Cura.RoundedRectangle.Direction.Left
|
||||
Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width
|
||||
Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width
|
||||
Layout.preferredWidth: parent.machineSelectorWidth
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
// Separator line
|
||||
Rectangle
|
||||
{
|
||||
height: parent.height
|
||||
width: UM.Theme.getSize("default_lining").width
|
||||
color: UM.Theme.getColor("lining")
|
||||
}
|
||||
|
||||
Cura.ConfigurationMenu
|
||||
{
|
||||
id: printerSetup
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width
|
||||
}
|
||||
|
||||
// Separator line
|
||||
Rectangle
|
||||
{
|
||||
height: parent.height
|
||||
width: UM.Theme.getSize("default_lining").width
|
||||
color: UM.Theme.getColor("lining")
|
||||
Layout.preferredWidth: parent.machineSelectorWidth * 2
|
||||
}
|
||||
|
||||
Item
|
||||
@ -91,22 +76,116 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
//Pop-up shown when there are multiple items to select from.
|
||||
Cura.ExpandablePopup
|
||||
{
|
||||
id: openFileMenu
|
||||
visible: prepareMenu.fileProviderModel.count > 1
|
||||
|
||||
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
|
||||
headerCornerSide: Cura.RoundedRectangle.Direction.All
|
||||
headerPadding: Math.round((parent.height - UM.Theme.getSize("button_icon").height) / 2)
|
||||
contentPadding: UM.Theme.getSize("default_lining").width
|
||||
enabled: visible
|
||||
|
||||
height: parent.height
|
||||
width: visible ? (headerPadding * 3 + UM.Theme.getSize("button_icon").height + iconSize) : 0
|
||||
|
||||
headerItem: UM.RecolorImage
|
||||
{
|
||||
id: menuIcon
|
||||
source: UM.Theme.getIcon("Folder", "medium")
|
||||
color: UM.Theme.getColor("icon")
|
||||
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
contentItem: Item
|
||||
{
|
||||
id: popup
|
||||
|
||||
Column
|
||||
{
|
||||
id: openProviderColumn
|
||||
|
||||
//The column doesn't automatically listen to its children rect if the children change internally, so we need to explicitly update the size.
|
||||
onChildrenRectChanged:
|
||||
{
|
||||
popup.height = childrenRect.height
|
||||
popup.width = childrenRect.width
|
||||
}
|
||||
onPositioningComplete:
|
||||
{
|
||||
popup.height = childrenRect.height
|
||||
popup.width = childrenRect.width
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: prepareMenu.fileProviderModel
|
||||
delegate: Button
|
||||
{
|
||||
leftPadding: UM.Theme.getSize("default_margin").width
|
||||
rightPadding: UM.Theme.getSize("default_margin").width
|
||||
width: contentItem.width + leftPadding + rightPadding
|
||||
height: UM.Theme.getSize("action_button").height
|
||||
hoverEnabled: true
|
||||
|
||||
contentItem: Label
|
||||
{
|
||||
text: model.displayText
|
||||
color: UM.Theme.getColor("text")
|
||||
font: UM.Theme.getFont("medium")
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
width: contentWidth
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
onClicked:
|
||||
{
|
||||
if(model.index == 0) //The 0th element is the "From Disk" option, which should activate the open local file dialog.
|
||||
{
|
||||
Cura.Actions.open.trigger();
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareMenu.fileProviderModel.trigger(model.name);
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
|
||||
radius: UM.Theme.getSize("action_button_radius").width
|
||||
width: popup.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If there is just a single item, show a button instead that directly chooses the one option.
|
||||
Button
|
||||
{
|
||||
id: openFileButton
|
||||
height: UM.Theme.getSize("stage_menu").height
|
||||
width: UM.Theme.getSize("stage_menu").height
|
||||
visible: prepareMenu.fileProviderModel.count <= 1
|
||||
|
||||
height: parent.height
|
||||
width: visible ? height : 0 //Square button (and don't take up space if invisible).
|
||||
onClicked: Cura.Actions.open.trigger()
|
||||
enabled: visible && prepareMenu.fileProviderModel.count > 0
|
||||
hoverEnabled: true
|
||||
|
||||
contentItem: Item
|
||||
{
|
||||
anchors.fill: parent
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: buttonIcon
|
||||
source: UM.Theme.getIcon("Folder", "medium")
|
||||
anchors.centerIn: parent
|
||||
source: UM.Theme.getIcon("load")
|
||||
width: UM.Theme.getSize("button_icon").width
|
||||
height: UM.Theme.getSize("button_icon").height
|
||||
color: UM.Theme.getColor("icon")
|
||||
@ -118,26 +197,14 @@ Item
|
||||
background: Rectangle
|
||||
{
|
||||
id: background
|
||||
height: UM.Theme.getSize("stage_menu").height
|
||||
width: UM.Theme.getSize("stage_menu").height
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
|
||||
radius: UM.Theme.getSize("default_radius").width
|
||||
color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
|
||||
}
|
||||
|
||||
DropShadow
|
||||
{
|
||||
id: shadow
|
||||
// Don't blur the shadow
|
||||
radius: 0
|
||||
anchors.fill: background
|
||||
source: background
|
||||
verticalOffset: 2
|
||||
visible: true
|
||||
color: UM.Theme.getColor("action_button_shadow")
|
||||
// Should always be drawn behind the background.
|
||||
z: background.z - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a prepare stage in Cura.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -24,54 +24,36 @@ Item
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
leftMargin: UM.Theme.getSize("wide_margin").width * 2
|
||||
rightMargin: UM.Theme.getSize("wide_margin").width * 2
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: stageMenuRow
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - 2 * UM.Theme.getSize("wide_margin").width
|
||||
height: parent.height
|
||||
anchors.fill: parent
|
||||
// This is a trick to make sure that the borders of the two adjacent buttons' borders overlap. Otherwise
|
||||
// there will be double border (one from each button)
|
||||
spacing: -UM.Theme.getSize("default_lining").width
|
||||
|
||||
Cura.ViewsSelector
|
||||
{
|
||||
id: viewsSelector
|
||||
height: parent.height
|
||||
width: UM.Theme.getSize("views_selector").width
|
||||
width: Math.max(Math.round((parent.width - printSetupSelectorItem.width) / 3), UM.Theme.getSize("views_selector").width)
|
||||
headerCornerSide: Cura.RoundedRectangle.Direction.Left
|
||||
}
|
||||
|
||||
// Separator line
|
||||
Rectangle
|
||||
{
|
||||
height: parent.height
|
||||
// If there is no viewPanel, we only need a single spacer, so hide this one.
|
||||
visible: viewPanel.source != ""
|
||||
width: visible ? UM.Theme.getSize("default_lining").width : 0
|
||||
|
||||
color: UM.Theme.getColor("lining")
|
||||
}
|
||||
|
||||
// This component will grow freely up to complete the width of the row.
|
||||
Loader
|
||||
{
|
||||
id: viewPanel
|
||||
height: parent.height
|
||||
width: source != "" ? (previewMenu.width - viewsSelector.width - printSetupSelectorItem.width - 2 * (UM.Theme.getSize("wide_margin").width + UM.Theme.getSize("default_lining").width)) : 0
|
||||
width: source != "" ? (parent.width - viewsSelector.width - printSetupSelectorItem.width) : 0
|
||||
source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : ""
|
||||
}
|
||||
|
||||
// Separator line
|
||||
Rectangle
|
||||
{
|
||||
height: parent.height
|
||||
width: UM.Theme.getSize("default_lining").width
|
||||
color: UM.Theme.getColor("lining")
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: printSetupSelectorItem
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a preview stage in Cura.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Provides removable drive hotplugging and writing support.",
|
||||
"version": "1.0.1",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -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.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ Item
|
||||
UM.SimpleButton
|
||||
{
|
||||
id: playButton
|
||||
iconSource: !isSimulationPlaying ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg"
|
||||
iconSource: !isSimulationPlaying ? "./resources/Play.svg": "./resources/Pause.svg"
|
||||
width: UM.Theme.getSize("small_button").width
|
||||
height: UM.Theme.getSize("small_button").height
|
||||
hoverColor: UM.Theme.getColor("slider_handle_active")
|
||||
@ -241,4 +241,4 @@ Item
|
||||
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,16 +203,16 @@ Cura.ExpandableComponent
|
||||
|
||||
style: UM.Theme.styles.checkbox
|
||||
|
||||
|
||||
UM.RecolorImage
|
||||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: extrudersModelCheckBox.right
|
||||
width: UM.Theme.getSize("layerview_legend_size").width
|
||||
height: UM.Theme.getSize("layerview_legend_size").height
|
||||
source: UM.Theme.getIcon("extruder_button")
|
||||
color: model.color
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides the Simulation view.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
4
plugins/SimulationView/resources/Pause.svg
Executable file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M8,21H6V3h2V21z M18,3h-2v18h2V3z" />
|
||||
</svg>
|
After Width: | Height: | Size: 167 B |
5
plugins/SimulationView/resources/Play.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M5,20V4c0-0.8,0.9-1.3,1.5-0.9l13,8c0.6,0.4,0.6,1.3,0,1.7l-13,8C5.9,21.3,5,20.8,5,20z M7,5.8v12.4
|
||||
L17.1,12L7,5.8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 247 B |
@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="6mm"
|
||||
height="6mm"
|
||||
viewBox="0 0 5.9999999 6"
|
||||
version="1.1"
|
||||
id="svg877"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="simulation_pause2.svg">
|
||||
<defs
|
||||
id="defs871" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="-5.3551409"
|
||||
inkscape:cy="17.386031"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1675"
|
||||
inkscape:window-x="-13"
|
||||
inkscape:window-y="-13"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-11.163774,-122.8006)">
|
||||
<g
|
||||
id="g825"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,10.185689,121.85192)">
|
||||
<rect
|
||||
y="5"
|
||||
x="19"
|
||||
height="20"
|
||||
width="2.7552757"
|
||||
id="rect5192"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.34745646;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<rect
|
||||
y="5"
|
||||
x="9"
|
||||
height="20"
|
||||
width="2.7552757"
|
||||
id="rect5192-5"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.34745646;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
@ -1,78 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="6mm"
|
||||
height="6mm"
|
||||
viewBox="0 0 6 6"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="simulation_resume2.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="-32.404712"
|
||||
inkscape:cy="14.267522"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1675"
|
||||
inkscape:window-x="-13"
|
||||
inkscape:window-y="-13"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(81.024887,-389.647)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.50520164;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path847"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="-78.732257"
|
||||
sodipodi:cy="392.65222"
|
||||
sodipodi:r1="3.0592039"
|
||||
sodipodi:r2="1.5296021"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -75.67305,392.65222 -4.588806,2.64935 v -5.2987 z"
|
||||
inkscape:transform-center-x="0.75529536"
|
||||
inkscape:transform-center-y="0.40090429" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
@ -87,8 +87,12 @@ class SliceInfo(QObject, Extension):
|
||||
return None
|
||||
file_path = os.path.join(plugin_path, "example_data.html")
|
||||
if file_path:
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
self._example_data_content = f.read()
|
||||
try:
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
self._example_data_content = f.read()
|
||||
except EnvironmentError as e:
|
||||
Logger.error(f"Unable to read example slice info data to show to the user: {e}")
|
||||
self._example_data_content = "<i>" + catalog.i18nc("@text", "Unable to read example data file.") + "</i>"
|
||||
return self._example_data_content
|
||||
|
||||
@pyqtSlot(bool)
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Submits anonymous slice info. Can be disabled through preferences.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a normal solid mesh view.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -11,7 +11,7 @@ def getMetaData():
|
||||
"tool": {
|
||||
"name": i18n_catalog.i18nc("@label", "Support Blocker"),
|
||||
"description": i18n_catalog.i18nc("@info:tooltip", "Create a volume in which supports are not printed."),
|
||||
"icon": "tool_icon.svg",
|
||||
"icon": "SupportBlocker",
|
||||
"weight": 4
|
||||
}
|
||||
}
|
||||
|
@ -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.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="30px" height="30px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
||||
<title>support_blocker</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="support_blocker" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group-3" transform="translate(3.000000, 2.000000)">
|
||||
<g id="Print-as-support-Copy" fill="#000">
|
||||
<path d="M0.833,19.65 L0.833,22.342 L0,22.3428571 L0.833,19.65 Z M4.166,8.879 L4.166,22.342 L2.499,22.342 L2.499,14.266 L4.166,8.879 Z M7.5,0.8 L7.5,22.342 L5.833,22.342 L5.833,0.8 L7.5,0.8 Z M10.833,0.8 L10.833,22.342 L9.166,22.342 L9.166,0.8 L10.833,0.8 Z M14.166,0.8 L14.166,14 L12.499,14 L12.499,0.8 L14.166,0.8 Z M15.833,8.879 L17.418,14 L15.833,14 L15.833,8.879 Z M4.166,0.8 L4.166,6.139 L2.499,1.351 L2.499,0.8 L4.166,0.8 Z M17.5,0.8 L17.5,1.351 L15.833,6.139 L15.833,0.8 L17.5,0.8 Z" id="Combined-Shape"></path>
|
||||
</g>
|
||||
<path d="M12,14 L22,14 L22,24 L12,24 L12,14 Z" id="Rectangle" stroke="#000" stroke-dasharray="1,1"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
@ -2,6 +2,6 @@
|
||||
"name": "Toolbox",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"description": "Find, manage and install new Cura packages."
|
||||
}
|
||||
|
6
plugins/Toolbox/resources/images/Shop.svg
Executable file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M19,3H5C3.3,3,2,4.3,2,6v3c0,1.5,0.8,2.7,2,3.4V22h16v-9.6c1.2-0.7,2-2,2-3.4V6C22,4.3,20.7,3,19,3z
|
||||
M10,5h4v4c0,1.1-0.9,2-2,2s-2-0.9-2-2V5z M4,9V5h4v4c0,1.1-0.9,2-2,2S4,10.1,4,9z M18,20h-4v-5h-4v5H6v-7c1.2,0,2.3-0.5,3-1.4
|
||||
c0.7,0.8,1.8,1.4,3,1.4s2.3-0.5,3-1.4c0.7,0.8,1.8,1.4,3,1.4V20z M20,9c0,1.1-0.9,2-2,2s-2-0.9-2-2V5h4V9z" />
|
||||
</svg>
|
After Width: | Height: | Size: 458 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<path d="M19,3H5A2.9,2.9,0,0,0,2,6V9a3.9,3.9,0,0,0,2,3.4V22H20V12.4A3.9,3.9,0,0,0,22,9V6A2.9,2.9,0,0,0,19,3ZM10,5h4V9a2,2,0,0,1-4,0ZM4,9V5H8V9A2,2,0,0,1,4,9ZM18,20H14V15H10v5H6V13a3.7,3.7,0,0,0,3-1.4A3.7,3.7,0,0,0,12,13a3.7,3.7,0,0,0,3-1.4A3.7,3.7,0,0,0,18,13ZM20,9a2,2,0,0,1-4,0V5h4Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 364 B |
@ -41,7 +41,7 @@ Item
|
||||
height: height
|
||||
}
|
||||
color: button.enabled ? (button.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
|
||||
source: UM.Theme.getIcon("arrow_left")
|
||||
source: UM.Theme.getIcon("ChevronSingleLeft")
|
||||
}
|
||||
width: UM.Theme.getSize("toolbox_back_button").width
|
||||
height: UM.Theme.getSize("toolbox_back_button").height
|
||||
|
@ -4,7 +4,7 @@
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Rectangle
|
||||
|
@ -95,7 +95,7 @@ Item
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: cloudMarketplaceButton
|
||||
source: "../../images/shop.svg"
|
||||
source: "../../images/Shop.svg"
|
||||
color: UM.Theme.getColor(webMarketplaceButtonTooltipArea.containsMouse ? "primary" : "text")
|
||||
height: parent.height / 2
|
||||
width: height
|
||||
|
@ -3,5 +3,5 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading model files.",
|
||||
"api": "7.5.0"
|
||||
"api": 7
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading Ultimaker Format Packages.",
|
||||
"supported_sdk_versions": ["7.5.0"],
|
||||
"supported_sdk_versions": ["7.6.0"],
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for writing Ultimaker Format Packages.",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Manages network connections to Ultimaker networked printers.",
|
||||
"version": "2.0.0",
|
||||
"api": "7.5.0",
|
||||
"api": 7,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -1,23 +1,26 @@
|
||||
// Copyright (c) 2019 Ultimaker B.V.
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Rectangle
|
||||
Button
|
||||
{
|
||||
id: base
|
||||
|
||||
property var enabled: true
|
||||
|
||||
property var iconSource: null
|
||||
color: enabled ? UM.Theme.getColor("monitor_icon_primary") : UM.Theme.getColor("monitor_icon_disabled")
|
||||
height: width
|
||||
radius: Math.round(0.5 * width)
|
||||
width: 24 * screenScaleFactor
|
||||
width: UM.Theme.getSize("button").width * 0.75 //Matching the size of the content of tool buttons.
|
||||
height: UM.Theme.getSize("button").height * 0.75
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
radius: 0.5 * width
|
||||
color: parent.enabled ? (parent.hovered ? UM.Theme.getColor("monitor_secondary_button_hover") : "transparent") : UM.Theme.getColor("monitor_icon_disabled")
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
@ -27,30 +30,21 @@ Rectangle
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: UM.Theme.getColor("monitor_icon_accent")
|
||||
color: UM.Theme.getColor("primary")
|
||||
height: width
|
||||
source: iconSource
|
||||
width: Math.round(parent.width / 2)
|
||||
}
|
||||
|
||||
MouseArea
|
||||
onClicked:
|
||||
{
|
||||
id: clickArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: base.enabled
|
||||
onClicked:
|
||||
if (OutputDevice.activeCameraUrl != "")
|
||||
{
|
||||
if (base.enabled)
|
||||
{
|
||||
if (OutputDevice.activeCameraUrl != "")
|
||||
{
|
||||
OutputDevice.setActiveCameraUrl("")
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDevice.setActiveCameraUrl(modelData.cameraUrl)
|
||||
}
|
||||
}
|
||||
OutputDevice.setActiveCameraUrl("")
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDevice.setActiveCameraUrl(modelData.cameraUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ Item
|
||||
id: buildplateIcon
|
||||
anchors.centerIn: parent
|
||||
color: UM.Theme.getColor("monitor_icon_primary")
|
||||
height: parent.height
|
||||
source: "../svg/icons/buildplate.svg"
|
||||
height: UM.Theme.getSize("medium_button_icon").width
|
||||
source: "../svg/icons/Buildplate.svg"
|
||||
width: height
|
||||
visible: buildplate
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ Item
|
||||
sourceSize.width: width // TODO: Theme!
|
||||
sourceSize.height: width // TODO: Theme!
|
||||
color: UM.Theme.getColor("text")
|
||||
source: UM.Theme.getIcon("arrow_left")
|
||||
source: UM.Theme.getIcon("ChevronSingleLeft")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,7 +177,7 @@ Item
|
||||
sourceSize.width: width // TODO: Theme!
|
||||
sourceSize.height: width // TODO: Theme!
|
||||
color: UM.Theme.getColor("text")
|
||||
source: UM.Theme.getIcon("arrow_right")
|
||||
source: UM.Theme.getIcon("ChevronSingleRight")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import QtQuick 2.2
|
||||
import QtQuick.Controls 2.0
|
||||
import UM 1.3 as UM
|
||||
|
||||
import Cura 1.6 as Cura
|
||||
|
||||
/**
|
||||
* This component comprises a colored extruder icon, the material name, and the
|
||||
* print core name. It is used by the MonitorPrinterConfiguration component with
|
||||
@ -18,10 +20,10 @@ import UM 1.3 as UM
|
||||
Item
|
||||
{
|
||||
// The material color
|
||||
property alias color: extruderIcon.color
|
||||
property alias color: extruderIcon.materialColor
|
||||
|
||||
// The extruder position; NOTE: Decent human beings count from 0
|
||||
property alias position: extruderIcon.position
|
||||
// The extruder position
|
||||
property int position
|
||||
|
||||
// The material name
|
||||
property alias material: materialLabel.text
|
||||
@ -32,12 +34,13 @@ Item
|
||||
// Height is 2 x 18px labels, plus 4px spacing between them
|
||||
height: 40 * screenScaleFactor // TODO: Theme!
|
||||
width: childrenRect.width
|
||||
opacity: material != "" && material != "Empty" && position >= 0 ? 1 : 0.4
|
||||
|
||||
MonitorIconExtruder
|
||||
Cura.ExtruderIcon
|
||||
{
|
||||
id: extruderIcon
|
||||
color: UM.Theme.getColor("monitor_skeleton_loading")
|
||||
position: 0
|
||||
materialColor: UM.Theme.getColor("monitor_skeleton_loading")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle
|
||||
@ -46,16 +49,18 @@ Item
|
||||
anchors
|
||||
{
|
||||
left: extruderIcon.right
|
||||
leftMargin: 12 * screenScaleFactor // TODO: Theme!
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
verticalCenter: extruderIcon.verticalCenter
|
||||
}
|
||||
color: materialLabel.visible > 0 ? "transparent" : UM.Theme.getColor("monitor_skeleton_loading")
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: childrenRect.height
|
||||
width: Math.max(materialLabel.contentWidth, 60 * screenScaleFactor) // TODO: Theme!
|
||||
radius: 2 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
Label
|
||||
{
|
||||
id: materialLabel
|
||||
anchors.top: parent.top
|
||||
|
||||
color: UM.Theme.getColor("text")
|
||||
elide: Text.ElideRight
|
||||
@ -63,29 +68,13 @@ Item
|
||||
text: ""
|
||||
visible: text !== ""
|
||||
|
||||
// FIXED-LINE-HEIGHT:
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: printCoreLabelWrapper
|
||||
anchors
|
||||
{
|
||||
left: materialLabelWrapper.left
|
||||
bottom: parent.bottom
|
||||
}
|
||||
color: printCoreLabel.visible > 0 ? "transparent" : UM.Theme.getColor("monitor_skeleton_loading")
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
width: Math.max(printCoreLabel.contentWidth, 36 * screenScaleFactor) // TODO: Theme!
|
||||
radius: 2 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
Label
|
||||
{
|
||||
id: printCoreLabel
|
||||
anchors.top: materialLabel.bottom
|
||||
|
||||
color: UM.Theme.getColor("text")
|
||||
elide: Text.ElideRight
|
||||
@ -93,9 +82,6 @@ Item
|
||||
text: ""
|
||||
visible: text !== ""
|
||||
|
||||
// FIXED-LINE-HEIGHT:
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ Item
|
||||
property int size: 32 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
// THe extruder icon source; NOTE: This shouldn't need to be changed
|
||||
property string iconSource: "../svg/icons/extruder.svg"
|
||||
property string iconSource: "../svg/icons/Extruder.svg"
|
||||
|
||||
height: size
|
||||
width: size
|
||||
@ -38,6 +38,7 @@ Item
|
||||
Label
|
||||
{
|
||||
id: positionLabel
|
||||
anchors.centerIn: icon
|
||||
font: UM.Theme.getFont("small")
|
||||
color: UM.Theme.getColor("text")
|
||||
height: Math.round(size / 2)
|
||||
@ -45,8 +46,6 @@ Item
|
||||
text: position + 1
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
width: Math.round(size / 2)
|
||||
x: Math.round(size * 0.25)
|
||||
y: Math.round(size * 0.15625)
|
||||
visible: position >= 0
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|