mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-19 20:29:40 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura into 3mf_platform_meshes
This commit is contained in:
commit
b7081f9c68
@ -29,10 +29,12 @@ class Account(QObject):
|
||||
# Signal emitted when user logged in or out.
|
||||
loginStateChanged = pyqtSignal(bool)
|
||||
accessTokenChanged = pyqtSignal()
|
||||
cloudPrintersDetectedChanged = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, application: "CuraApplication", parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._application = application
|
||||
self._new_cloud_printers_detected = False
|
||||
|
||||
self._error_message = None # type: Optional[Message]
|
||||
self._logged_in = False
|
||||
@ -74,6 +76,10 @@ class Account(QObject):
|
||||
def isLoggedIn(self) -> bool:
|
||||
return self._logged_in
|
||||
|
||||
@pyqtProperty(bool, notify=cloudPrintersDetectedChanged)
|
||||
def newCloudPrintersDetected(self) -> bool:
|
||||
return self._new_cloud_printers_detected
|
||||
|
||||
def _onLoginStateChanged(self, logged_in: bool = False, error_message: Optional[str] = None) -> None:
|
||||
if error_message:
|
||||
if self._error_message:
|
||||
|
@ -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.1.0"
|
||||
CuraSDKVersion = "7.2.0"
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraAppName # type: ignore
|
||||
|
@ -196,6 +196,7 @@ class Arrange:
|
||||
start_idx = 0
|
||||
else:
|
||||
start_idx = 0
|
||||
priority = 0
|
||||
for priority in self._priority_unique_values[start_idx::step]:
|
||||
tryout_idx = numpy.where(self._priority == priority)
|
||||
for idx in range(len(tryout_idx[0])):
|
||||
|
@ -171,6 +171,10 @@ class MachineNode(ContainerNode):
|
||||
if variant_name not in self.variants:
|
||||
self.variants[variant_name] = VariantNode(variant["id"], machine = self)
|
||||
self.variants[variant_name].materialsChanged.connect(self.materialsChanged)
|
||||
else:
|
||||
# Force reloading the materials if the variant already exists or else materals won't be loaded
|
||||
# when the G-Code flavor changes --> CURA-7354
|
||||
self.variants[variant_name]._loadAll()
|
||||
if not self.variants:
|
||||
self.variants["empty"] = VariantNode("empty_variant", machine = self)
|
||||
|
||||
|
@ -61,6 +61,7 @@ class PreviewPass(RenderPass):
|
||||
self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0])
|
||||
self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0])
|
||||
self._shader.setUniformValue("u_shininess", 20.0)
|
||||
self._shader.setUniformValue("u_renderError", 0.0) # We don't want any error markers!.
|
||||
self._shader.setUniformValue("u_faceId", -1) # Don't render any selected faces in the preview.
|
||||
|
||||
if not self._non_printing_shader:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
@ -206,8 +206,11 @@ class ContainerManager(QObject):
|
||||
if contents is None:
|
||||
return {"status": "error", "message": "Serialization returned None. Unable to write to file"}
|
||||
|
||||
with SaveFile(file_url, "w") as f:
|
||||
f.write(contents)
|
||||
try:
|
||||
with SaveFile(file_url, "w") as f:
|
||||
f.write(contents)
|
||||
except OSError:
|
||||
return {"status": "error", "message": "Unable to write to this location.", "path": file_url}
|
||||
|
||||
return {"status": "success", "message": "Successfully exported container", "path": file_url}
|
||||
|
||||
|
@ -94,6 +94,12 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
#
|
||||
# \return An extruder's position, or None if no position info is available.
|
||||
def getActiveExtruderPosition(self):
|
||||
# for support_meshes, always use the support_extruder
|
||||
if self.getStack().getProperty("support_mesh", "value"):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
return str(global_container_stack.getProperty("support_extruder_nr", "value"))
|
||||
|
||||
containers = ContainerRegistry.getInstance().findContainers(id = self.getActiveExtruder())
|
||||
if containers:
|
||||
container_stack = containers[0]
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM.Logger import Logger
|
||||
import re
|
||||
@ -38,6 +38,9 @@ class ObjectsModel(ListModel):
|
||||
OutsideAreaRole = Qt.UserRole + 3
|
||||
BuilplateNumberRole = Qt.UserRole + 4
|
||||
NodeRole = Qt.UserRole + 5
|
||||
PerObjectSettingsCountRole = Qt.UserRole + 6
|
||||
MeshTypeRole = Qt.UserRole + 7
|
||||
ExtruderNumberRole = Qt.UserRole + 8
|
||||
|
||||
def __init__(self, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
@ -46,6 +49,9 @@ class ObjectsModel(ListModel):
|
||||
self.addRoleName(self.SelectedRole, "selected")
|
||||
self.addRoleName(self.OutsideAreaRole, "outside_build_area")
|
||||
self.addRoleName(self.BuilplateNumberRole, "buildplate_number")
|
||||
self.addRoleName(self.ExtruderNumberRole, "extruder_number")
|
||||
self.addRoleName(self.PerObjectSettingsCountRole, "per_object_settings_count")
|
||||
self.addRoleName(self.MeshTypeRole, "mesh_type")
|
||||
self.addRoleName(self.NodeRole, "node")
|
||||
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSceneDelayed)
|
||||
@ -172,11 +178,47 @@ class ObjectsModel(ListModel):
|
||||
|
||||
node_build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||
|
||||
node_mesh_type = ""
|
||||
per_object_settings_count = 0
|
||||
|
||||
per_object_stack = node.callDecoration("getStack")
|
||||
if per_object_stack:
|
||||
per_object_settings_count = per_object_stack.getTop().getNumInstances()
|
||||
|
||||
for mesh_type in ["anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"]:
|
||||
if per_object_stack.getProperty(mesh_type, "value"):
|
||||
node_mesh_type = mesh_type
|
||||
per_object_settings_count -= 1 # do not count this mesh type setting
|
||||
break
|
||||
|
||||
if per_object_settings_count > 0:
|
||||
if node_mesh_type == "support_mesh":
|
||||
# support meshes only allow support settings
|
||||
per_object_settings_count = 0
|
||||
for key in per_object_stack.getTop().getAllKeys():
|
||||
if per_object_stack.getTop().getInstance(key).definition.isAncestor("support"):
|
||||
per_object_settings_count += 1
|
||||
elif node_mesh_type == "anti_overhang_mesh":
|
||||
# anti overhang meshes ignore per model settings
|
||||
per_object_settings_count = 0
|
||||
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
if extruder_position is None:
|
||||
extruder_number = -1
|
||||
else:
|
||||
extruder_number = int(extruder_position)
|
||||
if node_mesh_type == "anti_overhang_mesh" or node.callDecoration("isGroup"):
|
||||
# for anti overhang meshes and groups the extruder nr is irrelevant
|
||||
extruder_number = -1
|
||||
|
||||
nodes.append({
|
||||
"name": node.getName(),
|
||||
"selected": Selection.isSelected(node),
|
||||
"outside_build_area": is_outside_build_area,
|
||||
"buildplate_number": node_build_plate_number,
|
||||
"extruder_number": extruder_number,
|
||||
"per_object_settings_count": per_object_settings_count,
|
||||
"mesh_type": node_mesh_type,
|
||||
"node": node
|
||||
})
|
||||
|
||||
|
@ -243,6 +243,10 @@ class WelcomePagesModel(ListModel):
|
||||
{"id": "data_collections",
|
||||
"page_url": self._getBuiltinWelcomePagePath("DataCollectionsContent.qml"),
|
||||
},
|
||||
{"id": "cloud",
|
||||
"page_url": self._getBuiltinWelcomePagePath("CloudContent.qml"),
|
||||
"should_show_function": self.shouldShowCloudPage,
|
||||
},
|
||||
{"id": "add_network_or_local_printer",
|
||||
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
|
||||
"next_page_id": "machine_actions",
|
||||
@ -253,12 +257,8 @@ class WelcomePagesModel(ListModel):
|
||||
},
|
||||
{"id": "machine_actions",
|
||||
"page_url": self._getBuiltinWelcomePagePath("FirstStartMachineActionsContent.qml"),
|
||||
"next_page_id": "cloud",
|
||||
"should_show_function": self.shouldShowMachineActions,
|
||||
},
|
||||
{"id": "cloud",
|
||||
"page_url": self._getBuiltinWelcomePagePath("CloudContent.qml"),
|
||||
},
|
||||
]
|
||||
|
||||
pages_to_show = all_pages_list
|
||||
@ -287,6 +287,17 @@ class WelcomePagesModel(ListModel):
|
||||
first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id)
|
||||
return len([action for action in first_start_actions if action.needsUserInteraction()]) > 0
|
||||
|
||||
def shouldShowCloudPage(self) -> bool:
|
||||
"""
|
||||
The cloud page should be shown only if the user is not logged in
|
||||
|
||||
:return: True if the user is not logged in, False if he/she is
|
||||
"""
|
||||
# Import CuraApplication locally or else it fails
|
||||
from cura.CuraApplication import CuraApplication
|
||||
api = CuraApplication.getInstance().getCuraAPI()
|
||||
return not api.account.isLoggedIn
|
||||
|
||||
def addPage(self) -> None:
|
||||
pass
|
||||
|
||||
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
<mxfile host="www.draw.io" modified="2019-12-20T12:41:33.716Z" agent="Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0" etag="exp7abRcULgdJsv-qAei" version="12.4.3" type="device" pages="1"><diagram id="05EojhSyumsKE0fvOSX8" name="Page-1">7ZtNb9s4EIZ/jY+70LftY+NkuwskQFrvts2pYCRaIkqLBkXXcn79Di3Ssk3ZTR195EDAB82IpIZ8ZgzxBTXyZ8vyI0er7IElmI48JylH/u3I89zA80by5yTbyjOOxpUj5SRRjWrHnLxg5XSUd00SXBw1FIxRQVbHzpjlOY7FkQ9xzjbHzRaMHj91hVJsOOYxoqb3K0lEVnkn3rj2/41Jmuknu9G0urNEurGaSZGhhG0OXP7dyJ9xxkR1tSxnmMrF0+tS9fvrzN19YBzn4jUdvnDvJQzwv+sXOp0/lfHk+3fxh1+N8hPRtZrwXSn4OsF8LlD8Q0Uutno5ig1ZUpSDdbNguZirOw7YcUZoco+2bC3DKWR3bd1kjJMXaI8o3HLBAbe5ULS9SI5GKJ0xyjg4crZ7QN2piqV6DMcFdHvU03ZPXA+oPGp4jwqhA2SUolVBnnchy45LxFOS3zAh2FI1UuuBucDl2YV29/gg7zFbYsG30ER3mCriKuXdsbI3dQK5gfJlB8nj+YFKXJW06X7smitcKLS/gTkwMP9XYG7QhTmLHRzOfuATGg2AECVpDibFC9lNLhqByvmg3IKt5GArFJM8vd+1uQ1qz2c1celi0HdBd9WRkSTBueTHBBLoeZ9fK0ZysVuY8AZ+sH4z589wFELgM7Dd2oafbM7FjOUwF0R23DBkwgbLbHgd5PP1YpJXpCGTXwc66ohzaHD+tAZKQsY4y1Cewl+phd4u9NAbGHpkQP8nF3JGlnS7pMeTgUmPz5e3Rd0qatcJBmY9MVg/IIE5gVcoC7tl2P5rX9C6gj01YH9BgNr+h7fPOhz6HU3vbA9g3+IFyYkgLLfvad2Rnwz9oua6F8lb3u1uxpyhX9c8k/dHyp61nmG1lbdrK4GWEw8g+06v2opWOK248tZ6Psf+vLrSjLqzejbVUiuvdI69QV/pGbupnlqBpRvWDQpLz6wvKKgWdruwmzSWnmmb0qkVWTrD3aCy9Izb1E+tzNIV7QadpWfapoJqdZZe0DcILT2jN/VUK7R0uDFrUFr6Be6ae3B7juWtWosfvrtzLK65A7day1UlXVXM+z3J4pqbbqu1dI598LMsrrn/tlpLN6wHP83imrtvq7V0BHv48yyuufm2WktnuAc/0eKau2+rtXRFe/gzLZc33PaFrTP0gx9q0Z+3WK2ln43Z4KdafBO4gRnnyQf58RVYMUVFQWKpg4illk1gCfj2m1x2WE9lPikKO+O2PLK22iqJOOgG1pMeEa7rTtLQfargcGJ853Wig8AE2JrH+NLUq3YC8RRfo5cdMAsbkGkfxxQJ8vM43iaO6gmPMm9H58/I6BLVQ1TzVL3qbDAG8oLjgaLpyUDVQhgDAXq0PWim6upswF50ErDvXIzLDy62h4sqgjrH9wzekPbmYa7fT/sr0lcaj/CaDPFjflgUV5RPi6Wgc+qd10LknaTK+MpaiMa/KKqWaiE4DbjfWgCz/iK1al5/1+vf/Q8=</diagram></mxfile>
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
21
docs/index.md
Normal file
21
docs/index.md
Normal file
@ -0,0 +1,21 @@
|
||||
Cura Documentation
|
||||
====
|
||||
Welcome to the Cura documentation pages.
|
||||
|
||||
Objective
|
||||
----
|
||||
The goal of this documentation is to give an overview of the architecture of Cura's source code. The purpose of this overview is to make programmers familiar with Cura's source code so that they may contribute more easily, write plug-ins more easily or get started within the Cura team more quickly.
|
||||
|
||||
There are some caveats though. These are *not* within the scope of this documentation:
|
||||
* There is no documentation on individual functions or classes of the code here. For that, refer to the Doxygen documentation and Python Docstrings in the source code itself, or generate the documentation locally using Doxygen.
|
||||
* It's virtually impossible and indeed not worth the effort or money to keep this 100% up to date.
|
||||
* There are no example plug-ins here. There are a number of example plug-ins in the Ultimaker organisation on Github.com to draw from.
|
||||
* The slicing process is not documented here. Refer to CuraEngine for that.
|
||||
|
||||
This documentation will touch on the inner workings of Uranium as well though, due to the nature of the architecture.
|
||||
|
||||
Index
|
||||
----
|
||||
The following chapters are available in this documentation:
|
||||
* [Repositories](repositories.md): An overview of the repositories that together make up the Cura application.
|
||||
* [Profiles](profiles/profiles.md): About the setting and profile system of Cura.
|
33
docs/profiles/container_stacks.md
Normal file
33
docs/profiles/container_stacks.md
Normal file
@ -0,0 +1,33 @@
|
||||
Container Stacks
|
||||
====
|
||||
When the user selects the profiles and settings to print with, he can swap out a number of profiles. The profiles that are currently in use are stored in several container stacks. These container stacks always have a definition container at the bottom, which defines all available settings and all available properties for each setting. The profiles on top of that definition can then override the `value` property of some of those settings.
|
||||
|
||||
When deriving a setting value, a container stack starts looking at the top-most profile to see if it contains an override for that setting. If it does, it returns that override. Otherwise, it looks into the second profile. If that also doesn't have an override for this setting, it looks into the third profile, and so on. The last profile is always a definition container which always contains an value for all settings. This way, the profiles at the top will always win over the profiles at the bottom. There is a clear precedence order for which profile wins over which other profile.
|
||||
|
||||
A Machine Instance
|
||||
----
|
||||
A machine instance is a printer that the user has added to his configuration. It consists of multiple container stacks: One for global settings and one for each of the available extruders. This way, different extruders can contain different materials and quality profiles, for instance. The global stack contains a different set of profiles than the extruder stacks.
|
||||
|
||||
While Uranium defines no specific roles for the entries in a container stack, Cura defines rigid roles for each slot in a container stack. These are the layouts for the container stacks of an example printer with 2 extruders.
|
||||
|
||||

|
||||
|
||||
To expand on this a bit further, each extruder stack contains the following profiles:
|
||||
* A user profile, where extruder-specific setting changes are stored that are not (yet) saved to a custom profile. If the user changes a setting that can be adjusted per extruder (such as infill density) then it gets stored here. If the user adjusts a setting that is global it will immediately be stored in the user profile of the global stack.
|
||||
* A custom profile. If the user saves his setting changes to a custom profile, it gets moved from the user profile to here. Actually a "custom profile" as the user sees it consists of multiple profiles: one for each extruder and one for the global settings.
|
||||
* An intent profile. The user can select between several intents for his print, such as precision, strength, visual quality, etc. This may be empty as well, which indicates the "default" intent.
|
||||
* A quality profile. The user can select between several quality levels.
|
||||
* A material profile, where the user selects which material is loaded in this extruder.
|
||||
* A nozzle profile, where the user selects which nozzle is installed in this extruder.
|
||||
* Definition changes, which stores the changes that the user made for this extruder in the Printer Settings dialogue.
|
||||
* Extruder. The user is not able to swap this out. This is a definition that lists the extruder number for this extruder and optionally things that are fixed in the printer, such as the nozzle offset.
|
||||
|
||||
The global container stack contains the following profiles:
|
||||
* A user profile, where global setting changes are stored that are not (yet) saved to a custom profile. If the user changes for instance the layer height, the new value for the layer height gets stored here.
|
||||
* A custom profile. If the user saves his setting changes to a custom profile, the global settings that were in the global user profile get moved here.
|
||||
* An intent profile. Currently this must ALWAYS be empty. There are no global intent profiles. This is there for historical reasons.
|
||||
* A quality profile. This contains global settings that match with the quality level that the user selected. This global quality profile cannot be specific to a material or nozzle.
|
||||
* A material profile. Currently this must ALWAYS be empty. There are no global material profiles. This is there for historical reasons.
|
||||
* A variant profile. Currently this must ALWAYS be empty. There are no global variant profiles. This is there for historical reasons.
|
||||
* Definition changes, which stores the changes that the user made to the printer in the Printer Settings dialogue.
|
||||
* Printer. This specifies the currently used printer model, such as Ultimaker 3, Ultimaker S5, etc.
|
66
docs/profiles/getting_a_setting_value.md
Normal file
66
docs/profiles/getting_a_setting_value.md
Normal file
@ -0,0 +1,66 @@
|
||||
Getting a Setting Value
|
||||
====
|
||||
How Cura gets a setting's value is a complex endeavour that requires some explanation. The `value` property gets special treatment for this because there are a few other properties that influence the value. In this page we explain the algorithm to getting a setting value.
|
||||
|
||||
This page explains all possible cases for a setting, but not all of them may apply. For instance, a global setting will not evaluate the per-object settings to get its value. Exceptions to the rules for other types of settings will be written down.
|
||||
|
||||
Per Object Settings
|
||||
----
|
||||
Per-object settings, which are added to an object using the per-object settings tool, will always prevail over other setting values. They are not evaluated with the rest of the settings system because Cura's front-end doesn't need to send all setting values for all objects to CuraEngine separately. It only sends over the per-object settings that get overridden. CuraEngine then evaluates settings that can be changed per-object using the list of settings for that object but if the object doesn't have the setting attached falls back on the settings in the object's extruder. Refer to the [CuraEngine](#CuraEngine) chapter to see how this works.
|
||||
|
||||
Settings where the `settable_per_mesh` property is false will not be shown in Cura's interface in the list of available settings in the per-object settings panel. They cannot be adjusted per object then. CuraEngine will also not evaluate those settings for each object separately. There is (or should always be) a good reason why each of these settings are not evaluated per object: Simply because CuraEngine is not processing one particular mesh at that moment. For instance, when writing the move to change to the next layer, CuraEngine hasn't processed any of the meshes on that layer yet and so the layer change movement speed, or indeed the layer height, can't change for each object.
|
||||
|
||||
The per-object settings are stored in a separate container stack that is particular to the object. The container stack is added to the object via a scene decorator. It has just a single container in it, which contains all of the settings that the user changed.
|
||||
|
||||
Resolve
|
||||
----
|
||||
If the setting is not listed in the per-object settings, it needs to be evaluated from the main settings list. However before evaluating it from a particular extruder, Cura will check if the setting has the `resolve` property. If it does, it returns the output of the `resolve` property and that's everything.
|
||||
|
||||
The `resolve` property is intended for settings which are global in nature, but still need to be influenced by extruder-specific settings. A good example is the Build Plate Temperature, which is very dependent on the material(s) used by the printer, but there can only be a single bed temperature at a time.
|
||||
|
||||
Cura will simply evaluate the `resolve` setting if present, which is an arbitrary Python expression, and return its result as the setting's value. However typically the `resolve` property is a function that takes the values of this setting for all extruders in use and then computes a result based on those. There is a built-in function for that called `extruderValues()`, which returns a list of setting values, one for each extruder. The function can then for instance take the average of those. In the case of the build plate temperature it will take the highest of those. In the case of the adhesion type it will choose "raft" if any extruder uses a raft, or "brim" as second choice, "skirt" as third choice and "none" only if all extruders use "none". Each setting with a `resolve` property has its own way of resolving the setting. The `extruderValues()` function continues with the algorithm as written below, but repeats it for each extruder.
|
||||
|
||||
Limit To Extruder
|
||||
----
|
||||
If a setting is evaluated from a particular extruder stack, it normally gets evaluated from the extruder that the object is assigned to. However there are some exceptions. Some groups of settings belong to a particular "extruder setting", like the Infill Extruder setting, or the Support Extruder setting. Which extruder a setting belongs to is stored in the `limit_to_extruder` property. Settings which have their `limit_to_extruder` property set to `adhesion_extruder_nr`, for instance, belong to the build plate adhesion settings.
|
||||
|
||||
If the `limit_to_extruder` property evaluates to a positive number, instead of getting the setting from the object's extruder it will be obtained from the extruder written in the `limit_to_extruder` property. So even if an object is set to be printed with extruder 0, if the infill extruder is set to extruder 1 any infill setting will be obtained from extruder 1. If `limit_to_extruder` is negative (in particular -1, which is the default), then the setting will be obtained from the object's own extruder.
|
||||
|
||||
This property is communicated to CuraEngine separately. CuraEngine makes sure that the setting is evaluated from the correct extruder. Refer to the [CuraEngine](#CuraEngine) chapter to see how this works.
|
||||
|
||||
Evaluating a Stack
|
||||
----
|
||||
After the resolve and limit to extruder properties have been checked, the setting value needs to be evaluated from an extruder stack.
|
||||
|
||||
This is explained in more detail in the [Container Stacks](container_stacks.md) documentation. In brief, Cura will check the highest container in the extruder stack first to see whether that container overrides the setting. If it does, it returns that as the setting value. Otherwise, it checks the second container on the stack to see if that one overrides it. If it does it returns that value, and otherwise it checks the third container, and so on. If a setting is not overridden by any container in the extruder stack, it continues downward in the global stack. If it is also not overridden there, it eventually arrives at the definition in the bottom of the global stack.
|
||||
|
||||
Evaluating a Definition
|
||||
----
|
||||
If the evaluation for a setting reaches the last entry of the global stack, its definition, a few more things can happen.
|
||||
|
||||
Definition containers have an inheritance structure. For instance, the `ultimaker3` definition container specifies in its metadata that it inherits from `ultimaker`, which in turn inherits from `fdmprinter`. So again here, when evaluating a property from the `ultimaker3` definition it will first look to see if the property is overridden by the `ultimaker3` definition itself, and otherwise refer on to the `ultimaker` definition or otherwise finally to the `fdmprinter` definition. `fdmprinter` is the last line of defence, and it contains *all* properties for *all* settings.
|
||||
|
||||
But even in `fdmprinter`, not all settings have a `value` property. It is not a required property. If the setting doesn't have a `value` property, the `default_value` property is returned, which is a required property. The distinction between `value` and `default_value` is made in order to allow CuraEngine to load a definition file as well when running from the command line (a debugging technique for CuraEngine). It then won't have all of the correct setting values but it at least doesn't need to evaluate all of the Python expressions and you'll be able to make some debugging slices.
|
||||
|
||||
Evaluating a Value Property
|
||||
----
|
||||
The `value` property may contain a formula, which is an arbitrary Python expression that will be executed by Cura to arrive at a setting value. All containers may set the `value` property. Instance containers can only set the `value`, while definitions can set all properties.
|
||||
|
||||
While the value could be any sort of formula, some functions of Python are restricted for security reasons. Since Cura 4.6, profiles are no longer a "trusted" resource and are therefore subject to heavy restrictions. It can use Python's built in mathematical functions and list functions as well as a few basic other ones, but things like writing to a file are prohibited.
|
||||
|
||||
There are also a few extra things that can be used in these expressions:
|
||||
* Any setting key can be used as a variable that contains the setting's value.
|
||||
* As explained before, `extruderValues(key)` is a function that returns a list of setting values for a particular setting for all used extruders.
|
||||
* The function `extruderValue(extruder, key)` will evaluate a particular setting for a particular extruder.
|
||||
* The function `resolveOrValue(key)` will perform the full setting evaluation as described in this document for the current context (so if this setting is being evaluated for the second extruder it would perform it as if coming from the second extruder).
|
||||
* The function `defaultExtruderPosition()` will get the first extruder that is not disabled. For instance, if a printer has three extruders but the first is disabled, this would return `1` to indicate the second extruder (0-indexed).
|
||||
* The function `valueFromContainer(key, index)` will get a setting value from the global stack, but skip the first few containers in that stack. It will skip until it reaches a particular index in the container stack.
|
||||
* The function `valueFromExtruderContainer(key, index)` will get a setting value from the current extruder stack, but skip the first few containers in that stack. It will skip until it reaches a particular index in the container stack.
|
||||
|
||||
CuraEngine
|
||||
----
|
||||
When starting a slice, Cura will send the scene to CuraEngine and with each model send over the per-object settings that belong to it. It also sends all setting values over, as evaluated from each extruder and from the global stack, and sends the `limit_to_extruder` property along as well. CuraEngine stores this and then starts its slicing process. CuraEngine also has a hierarchical structure for its settings with fallbacks. This is explained in detail in [the documentation of CuraEngine](https://github.com/Ultimaker/CuraEngine/blob/master/docs/settings.md) and shortly again here.
|
||||
|
||||
Each model gets a setting container assigned. The per-object settings are stored in those. The fallback for this container is set to be the extruder with which the object is printed. The extruder uses the current *mesh group* as fallback (which is a concept that Cura's front-end doesn't have). Each mesh group uses the global settings container as fallback.
|
||||
|
||||
During the slicing process CuraEngine will evaluate the settings from its current context as it goes. For instance, when processing the walls for a particular mesh, it will request the Outer Wall Line Width setting from the settings container of that mesh. When it's not processing a particular mesh but for instance the travel moves between two meshes, it uses the currently applicable extruder. So this business logic defines actually how a setting can be configured per mesh, per extruder or only globally. The `settable_per_extruder`, and related properties of settings are only used in the front-end to determine how the settings are shown to the user.
|
30
docs/profiles/profiles.md
Normal file
30
docs/profiles/profiles.md
Normal file
@ -0,0 +1,30 @@
|
||||
Profiles
|
||||
====
|
||||
Cura's profile system is very advanced and has gotten pretty complex. This chapter is an attempt to document how it is structured.
|
||||
|
||||
Index
|
||||
----
|
||||
The following pages describe the profile and setting system of Cura:
|
||||
* [Container Stacks](container_stacks.md): Which profiles can be swapped out and how they are ordered when evaluating a setting.
|
||||
* [Setting Properties](setting_properties.md): What properties can each setting have?
|
||||
* [Getting a Setting Value](getting_a_setting_value.md): How Cura arrives at a value for a certain setting.
|
||||
|
||||
Glossary
|
||||
----
|
||||
The terminology for these profiles is not always obvious. Here is a glossary of the terms that we'll use in this chapter.
|
||||
* **Profile:** Either an *instance container* or a *definition container*.
|
||||
* **Definition container:** Profile that's stored as .def.json file, defining new settings and all of their properties. In Cura these represent printer models and extruder trains.
|
||||
* **Instance container:** Profile that's stored as .inst.cfg file or .xml.fdm_material file, which override some setting values. In Cura these represent the other profiles.
|
||||
* **[Container] stack:** A list of profiles, with one definition container at the bottom and instance containers for the rest. All settings are defined in the definition container. The rest of the profiles each specify a set of value overrides. The profiles at the top always override the profiles at the bottom.
|
||||
* **Machine instance:** An instance of a printer that the user has added. The list of all machine instances is shown in a drop-down in Cura's interface.
|
||||
* **Material:** A type of filament that's being sold by a vendor as a product.
|
||||
* **Filament spool:** A single spool of material.
|
||||
* **Quality profile:** A profile that is one of the options when the user selects which quality level they want to print with.
|
||||
* **Intent profile:** A profile that is one of the options when the user selects what his intent is.
|
||||
* **Custom profile:** A user-made profile that is stored when the user selects to "create a profile from the current settings/overrides".
|
||||
* **Quality-changes profile:** Alternative name for *custom profile*. This name is used in the code more often, but it's a bit misleading so this documentation prefers the term "custom profile".
|
||||
* **User profile:** A profile containing the settings that the user has changed, but not yet saved to a profile.
|
||||
* **Variant profile:** A profile containing some overrides that allow the user to select variants of the definition. As of this writing this is only used for the nozzles.
|
||||
* **Quality level:** A measure of quality where the user can select from, for instance "normal", "fast", "high". When selecting a quality level, Cura will select a matching quality profile for each extruder.
|
||||
* **Quality type:** Alternative name for *quality level*. This name is used in the code more often, but this documentation prefers the term "quality level".
|
||||
* **Inheritance function:** A function through which the `value` of a setting is calculated. This may depend on other settings.
|
33
docs/profiles/setting_properties.md
Normal file
33
docs/profiles/setting_properties.md
Normal file
@ -0,0 +1,33 @@
|
||||
Setting Properties
|
||||
====
|
||||
Each setting in Cura has a number of properties. It's not just a key and a value. This page lists the properties that a setting can define.
|
||||
|
||||
* `key` (string): The identifier by which the setting is referenced. This is not a human-readable name, but just a reference string, such as `layer_height_0`. Typically these are named with the most significant category first, in order to sort them better, such as `material_print_temperature`. This is not actually a real property but just an identifier; it can't be changed.
|
||||
* `value` (optional): The current value of the setting. This can be a function, an arbitrary Python expression that depends on the values of other settings. If it's not present, the `default_value` is used.
|
||||
* `default_value`: A default value for the setting if `value` is undefined. This property is required however. It can't be a Python expression, but it can be any JSON type. This is made separate so that CuraEngine can read it out as well for its debugging mode via the command line, without needing a complete Python interpreter.
|
||||
* `label` (string): The human-readable name for the setting. This label is translated.
|
||||
* `description` (string): A longer description of what the setting does when you change it. This description is translated as well.
|
||||
* `type` (string): The type of value that this setting contains. Allowed types are: `bool`, `str`, `float`, `int`, `enum`, `category`, `[int]`, `vec3`, `polygon` and `polygons`.
|
||||
* `unit` (optional string): A unit that is displayed at the right-hand side of the text field where the user enters the setting value.
|
||||
* `resolve` (optional string): A Python expression that resolves disagreements for global settings if multiple per-extruder profiles define different values for a setting. Typically this takes the values for the setting from all stacks and computes one final value for it that will be used for the global setting. For instance, the `resolve` function for the build plate temperature is `max(extruderValues('material_bed_temperature')`, meaning that it will use the hottest bed temperature of all materials of the extruders in use.
|
||||
* `limit_to_extruder` (optional): A Python expression that indicates which extruder a setting will be obtained from. This is used for settings that may be extruder-specific but the extruder is not necessarily the current extruder. For instance, support settings need to be evaluated for the support extruder. Infill settings need to be evaluated for the infill extruder if the infill extruder is changed.
|
||||
* `enabled` (optional string or boolean): Whether the setting can currently be made visible for the user. This can be a simple true/false, or a Python expression that depends on other settings. Typically used for settings that don't apply when another setting is disabled, such as to hide the support settings if support is disabled.
|
||||
* `minimum_value` (optional): The lowest acceptable value for this setting. If it's any lower, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a layer height of 0mm. This property only applies to numerical settings.
|
||||
* `maximum_value` (optional): The highest acceptable value for this setting. If it's any higher, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a support overhang angle of more than 90 degrees. This property only applies to numerical settings.
|
||||
* `minimum_value_warning` (optional): The threshold under which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a low setting value. This property only applies to numerical settings.
|
||||
* `maximum_value_warning` (optional): The threshold above which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a high setting value. This property only applies to numerical settings.
|
||||
* `settable_globally` (optional boolean): Whether the setting can be changed globally. For some mesh-type settings such as `support_mesh` this doesn't make sense, so those can't be changed globally. They are not displayed in the main settings list then.
|
||||
* `settable_per_meshgroup` (optional boolean): Whether a setting can be changed per group of meshes. Currently unused in Cura.
|
||||
* `settable_per_extruder` (optional boolean): Whether a setting can be changed per extruder. Some settings, like the build plate temperature, can't be adjusted separately for each extruder. An icon is shown in the interface to indicate this. If the user changes these settings they are stored in the global stack.
|
||||
* `settable_per_mesh` (optional boolean): Whether a setting can be changed per mesh. The settings that can be changed per mesh are shown in the list of available settings in the per-object settings tool.
|
||||
* `children` (optional list): A list of child settings. These are displayed with an indentation. If all child settings are overridden by the user, the parent setting gets greyed out to indicate that the parent setting has no effect any more. This is not strictly always the case though, because that would depend on the inheritance functions in the `value`.
|
||||
* `icon` (optional string): A path to an icon to be displayed. Only applies to setting categories.
|
||||
* `allow_empty` (optional bool): Whether the setting is allowed to be empty. If it's not, this will be treated as a setting error and Cura will not allow the user to slice. Only applies to string-type settings.
|
||||
* `warning_description` (optional string): A warning message to display when the setting has a warning value. This is currently unused by Cura.
|
||||
* `error_description` (optional string): An error message to display when the setting has an error value. This is currently unused by Cura.
|
||||
* `options` (dictionary): A list of values that the user can choose from. The keys of this dictionary are keys that CuraEngine identifies the option with. The values are human-readable strings and will be translated. Only applies to (and only required for) enum-type settings.
|
||||
* `comments` (optional string): Comments to other programmers about the setting. This is not used by Cura.
|
||||
* `is_uuid` (optional boolean): Whether or not this setting indicates a UUID-4. If it is, the setting will indicate an error if it's not in the correct format. Only applies to string-type settings.
|
||||
* `regex_blacklist_pattern` (optional string): A regular expression, where if the setting value matches with this regular expression, it gets an error state. Only applies to string-type settings.
|
||||
* `error_value` (optional): If the setting value is equal to this value, it will show a setting error. This is used to display errors for non-numerical settings such as checkboxes.
|
||||
* `warning_value` (optional): If the setting value is equal to this value, it will show a setting warning. This is used to display warnings for non-numerical settings such as checkboxes.
|
21
docs/repositories.md
Normal file
21
docs/repositories.md
Normal file
@ -0,0 +1,21 @@
|
||||
Repositories
|
||||
====
|
||||
Cura uses a number of repositories where parts of our source code are separated, in order to get a cleaner architecture. Those repositories are:
|
||||
* [Cura](https://github.com/Ultimaker/Cura), the main repository for the front-end of Cura. This contains all of the business logic for the front-end, including the specific types of profiles that are available, the concept of 3D printers and materials, specific tools for handling 3D printed models, pretty much all of the GUI, as well as Ultimaker services such as the Marketplace and accounts.
|
||||
* The Cura repository is built on [Uranium](https://github.com/Ultimaker/Uranium), a framework for desktop applications that handle 3D models and have a separate back-end. This provides Cura with a basic GUI framework ([Qt](https://www.qt.io/)), a 3D scene, a rendering system, a plug-in system and a system for stacked profiles that change settings.
|
||||
* In order to slice, Cura starts [CuraEngine](https://github.com/Ultimaker/CuraEngine) in the background. This does the actual process that converts 3D models into a toolpath for the printer.
|
||||
* Communication to CuraEngine goes via [libArcus](https://github.com/Ultimaker/libArcus), a small library that wraps around [Protobuf](https://developers.google.com/protocol-buffers/) in order to make it run over a local socket.
|
||||
* Cura's build scripts are in [cura-build](https://github.com/Ultimaker/cura-build) and build scripts for building dependencies are in [cura-build-environment](https://github.com/Ultimaker/cura-build-environment).
|
||||
|
||||
There are also a number of repositories under our control that are not integral parts of Cura's architecture, but more like separated side-gigs:
|
||||
* Loading and writing 3MF files is done through [libSavitar](https://github.com/Ultimaker/libSavitar).
|
||||
* Loading and writing UFP files is done through [libCharon](https://github.com/Ultimaker/libCharon).
|
||||
* To make the build system a bit simpler, some parts are pre-compiled in [cura-binary-data](https://github.com/Ultimaker/cura-binary-data). This holds things like the machine-readable translation files and the Marlin builds for firmware updates, which would require considerable tooling to build automatically.
|
||||
* There are automated GUI tests in [Cura-squish-tests](https://github.com/Ultimaker/Cura-squish-tests).
|
||||
* Material profiles are stored in [fdm_materials](https://github.com/Ultimaker/fdm_materials). This is separated out and combined in our build process, so that the firmware for Ultimaker's printers can use the same set of profiles too.
|
||||
|
||||
Interplay
|
||||
----
|
||||
At a very high level, Cura's repositories interconnect as follows:
|
||||
|
||||

|
55
docs/resources/machine_instance.svg
Normal file
55
docs/resources/machine_instance.svg
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="1010">
|
||||
<defs>
|
||||
<path id="stack-header" d="m0,50 v-30 a20,20 0 0 1 20,-20 h260 a20,20 0 0 1 20,20 v30 z" />
|
||||
<marker id="arrow" refX="2" refY="1.5" markerWidth="3" markerHeight="3" orient="auto-start-reverse">
|
||||
<polygon points="0,0 3,1.5 0,3" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<g stroke="black" stroke-width="5" fill="silver"> <!-- Stack headers. -->
|
||||
<use href="#stack-header" x="200" y="555" />
|
||||
<use href="#stack-header" x="5" y="5" />
|
||||
<use href="#stack-header" x="395" y="5" />
|
||||
</g>
|
||||
<g stroke="black" stroke-width="10" fill="none"> <!-- Stack outlines. -->
|
||||
<rect x="200" y="555" width="300" height="450" rx="20" /> <!-- Global stack. -->
|
||||
<rect x="5" y="5" width="300" height="450" rx="20" /> <!-- Left extruder. -->
|
||||
<rect x="395" y="5" width="300" height="450" rx="20" /> <!-- Right extruder. -->
|
||||
</g>
|
||||
<g font-family="sans-serif" font-size="25" dominant-baseline="middle" text-anchor="middle">
|
||||
<text x="350" y="582.5">Global stack</text> <!-- Slightly lowered since the top line is thicker than the bottom. -->
|
||||
<text x="350" y="630">User</text>
|
||||
<text x="350" y="680">Custom</text>
|
||||
<text x="350" y="730">Intent</text>
|
||||
<text x="350" y="780">Quality</text>
|
||||
<text x="350" y="830">Material</text>
|
||||
<text x="350" y="880">Variant</text>
|
||||
<text x="350" y="930">Definition changes</text>
|
||||
<text x="350" y="980">Printer</text>
|
||||
|
||||
<text x="155" y="32.5">Left extruder</text> <!-- Slightly lowered again. -->
|
||||
<text x="155" y="80">User</text>
|
||||
<text x="155" y="130">Custom</text>
|
||||
<text x="155" y="180">Intent</text>
|
||||
<text x="155" y="230">Quality</text>
|
||||
<text x="155" y="280">Material</text>
|
||||
<text x="155" y="330">Nozzle</text>
|
||||
<text x="155" y="380">Definition changes</text>
|
||||
<text x="155" y="430">Extruder</text>
|
||||
|
||||
<text x="545" y="32.5">Right extruder</text> <!-- Slightly lowered again. -->
|
||||
<text x="545" y="80">User</text>
|
||||
<text x="545" y="130">Custom</text>
|
||||
<text x="545" y="180">Intent</text>
|
||||
<text x="545" y="230">Quality</text>
|
||||
<text x="545" y="280">Material</text>
|
||||
<text x="545" y="330">Nozzle</text>
|
||||
<text x="545" y="380">Definition changes</text>
|
||||
<text x="545" y="430">Extruder</text>
|
||||
</g>
|
||||
<g stroke="black" stroke-width="5" marker-end="url(#arrow)"> <!-- Arrows. -->
|
||||
<line x1="155" y1="455" x2="345" y2="545" />
|
||||
<line x1="545" y1="455" x2="355" y2="545" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
70
docs/resources/repositories.svg
Normal file
70
docs/resources/repositories.svg
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
|
||||
<defs>
|
||||
<marker id="arrow" refX="2" refY="1.5" markerWidth="3" markerHeight="3" orient="auto-start-reverse">
|
||||
<polygon points="0,0 3,1.5 0,3" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<g marker-end="url(#arrow)" stroke="black" stroke-width="5"> <!-- Arrows. -->
|
||||
<!-- Towards CuraEngine and back. -->
|
||||
<line x1="475" y1="400" x2="475" y2="307.5" />
|
||||
<line x1="475" y1="250" x2="475" y2="210" />
|
||||
<line x1="525" y1="200" x2="525" y2="242.5" />
|
||||
<line x1="525" y1="300" x2="525" y2="390" />
|
||||
|
||||
<!-- From libSavitar. -->
|
||||
<line x1="100" y1="425" x2="142.5" y2="425" />
|
||||
<line x1="300" y1="425" x2="390" y2="425" />
|
||||
|
||||
<!-- From fdm_materials. -->
|
||||
<line x1="350" y1="575" x2="390" y2="575" />
|
||||
|
||||
<!-- To libCharon. -->
|
||||
<line x1="600" y1="500" x2="692.5" y2="500" />
|
||||
<line x1="900" y1="500" x2="945" y2="500" />
|
||||
|
||||
<!-- To Uranium. -->
|
||||
<line x1="500" y1="600" x2="500" y2="690" />
|
||||
</g>
|
||||
|
||||
<g stroke="black" fill="none"> <!-- Boxes representing repositories. -->
|
||||
<g stroke-width="10"> <!-- Major repositories. -->
|
||||
<rect x="400" y="400" width="200" height="200" rx="20" /> <!-- Cura. -->
|
||||
<rect x="350" y="700" width="300" height="200" rx="20" /> <!-- Uranium. -->
|
||||
<rect x="300" y="5" width="400" height="195" rx="20" /> <!-- CuraEngine. -->
|
||||
</g>
|
||||
<g stroke-width="5"> <!-- Minor repositories. -->
|
||||
<rect x="150" y="350" width="150" height="100" rx="20" /> <!-- libSavitar. -->
|
||||
<rect x="100" y="550" width="250" height="100" rx="20" /> <!-- fdm_materials. -->
|
||||
<rect x="430" y="250" width="140" height="50" rx="20" /> <!-- libArcus. -->
|
||||
<rect x="700" y="450" width="200" height="100" rx="20" /> <!-- libCharon. -->
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<g font-family="sans-serif" text-anchor="middle" dominant-baseline="middle"> <!-- Labels. -->
|
||||
<g font-size="50"> <!-- Major repositories. -->
|
||||
<text x="500" y="500">Cura</text>
|
||||
<text x="500" y="800">Uranium</text>
|
||||
<text x="500" y="102.5">CuraEngine</text>
|
||||
</g>
|
||||
<g font-size="25"> <!-- Minor repositories and arrows. -->
|
||||
<text x="225" y="400">libSavitar</text>
|
||||
<text x="225" y="600">fdm_materials</text>
|
||||
<text x="500" y="275">libArcus</text>
|
||||
<text x="800" y="500">libCharon</text>
|
||||
|
||||
<g text-anchor="start">
|
||||
<text x="645" y="490" transform="rotate(-90, 645, 490)">G-code</text>
|
||||
<text x="950" y="500">UFP</text>
|
||||
<text x="535" y="345">G-code</text>
|
||||
<text x="345" y="415" transform="rotate(-90, 345, 415)">Model</text>
|
||||
<text x="510" y="645">Built upon</text>
|
||||
</g>
|
||||
<g text-anchor="end">
|
||||
<text x="465" y="345">Scene</text>
|
||||
<text x="90" y="425">3MF</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -738,11 +738,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
@staticmethod
|
||||
def _loadMetadata(file_name: str) -> Dict[str, Dict[str, Any]]:
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
result = dict() # type: Dict[str, Dict[str, Any]]
|
||||
try:
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
except zipfile.BadZipFile:
|
||||
Logger.logException("w", "Unable to retrieve metadata from {fname}: 3MF archive is corrupt.".format(fname = file_name))
|
||||
return result
|
||||
|
||||
metadata_files = [name for name in archive.namelist() if name.endswith("plugin_metadata.json")]
|
||||
|
||||
result = dict()
|
||||
|
||||
for metadata_file in metadata_files:
|
||||
try:
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for reading 3MF files.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for writing 3MF files.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,5 +3,5 @@
|
||||
"author": "fieldOfView",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading AMF files.",
|
||||
"api": "7.1.0"
|
||||
"api": "7.2.0"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Backup and restore your configuration.",
|
||||
"version": "1.2.0",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -720,9 +720,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||
## Creates a new socket connection.
|
||||
def _createSocket(self, protocol_file: str = None) -> None:
|
||||
if not protocol_file:
|
||||
if not self.getPluginId():
|
||||
Logger.error("Can't create socket before CuraEngineBackend plug-in is registered.")
|
||||
return
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if not plugin_path:
|
||||
Logger.log("e", "Could not get plugin path!", self.getPluginId())
|
||||
Logger.error("Could not get plugin path!", self.getPluginId())
|
||||
return
|
||||
protocol_file = os.path.abspath(os.path.join(plugin_path, "Cura.proto"))
|
||||
super()._createSocket(protocol_file)
|
||||
|
@ -13,6 +13,8 @@ from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Settings.ContainerStack import ContainerStack #For typing.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingDefinition import SettingDefinition
|
||||
from UM.Settings.SettingRelation import SettingRelation #For typing.
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
@ -103,20 +105,33 @@ class StartSliceJob(Job):
|
||||
## Check if a stack has any errors.
|
||||
## returns true if it has errors, false otherwise.
|
||||
def _checkStackForErrors(self, stack: ContainerStack) -> bool:
|
||||
if stack is None:
|
||||
return False
|
||||
|
||||
# if there are no per-object settings we don't need to check the other settings here
|
||||
stack_top = stack.getTop()
|
||||
if stack_top is None or not stack_top.getAllKeys():
|
||||
return False
|
||||
top_of_stack = cast(InstanceContainer, stack.getTop()) # Cache for efficiency.
|
||||
changed_setting_keys = top_of_stack.getAllKeys()
|
||||
|
||||
for key in stack.getAllKeys():
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
|
||||
Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
|
||||
# Add all relations to changed settings as well.
|
||||
for key in top_of_stack.getAllKeys():
|
||||
instance = top_of_stack.getInstance(key)
|
||||
if instance is None:
|
||||
continue
|
||||
self._addRelations(changed_setting_keys, instance.definition.relations)
|
||||
Job.yieldThread()
|
||||
|
||||
for changed_setting_key in changed_setting_keys:
|
||||
validation_state = stack.getProperty(changed_setting_key, "validationState")
|
||||
|
||||
if validation_state is None:
|
||||
definition = cast(SettingDefinition, stack.getSettingDefinition(changed_setting_key))
|
||||
validator_type = SettingDefinition.getValidatorForType(definition.type)
|
||||
if validator_type:
|
||||
validator = validator_type(changed_setting_key)
|
||||
validation_state = validator(stack)
|
||||
if validation_state in (
|
||||
ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
|
||||
Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", changed_setting_key, validation_state)
|
||||
return True
|
||||
Job.yieldThread()
|
||||
|
||||
return False
|
||||
|
||||
## Runs the job that initiates the slicing.
|
||||
@ -511,4 +526,3 @@ class StartSliceJob(Job):
|
||||
|
||||
relations_set.add(relation.target.key)
|
||||
self._addRelations(relations_set, relation.target.relations)
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "CuraEngine Backend",
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Provides the link to the CuraEngine slicing backend.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides support for exporting Cura profiles.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog":"cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Checks for firmware updates.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Writes g-code to a file.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class ModelChecker(QObject, Extension):
|
||||
|
||||
if material_shrinkage[node_extruder_position] > shrinkage_threshold:
|
||||
bbox = node.getBoundingBox()
|
||||
if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z:
|
||||
if bbox is not None and (bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z):
|
||||
warning_nodes.append(node)
|
||||
|
||||
self._caution_message.setText(catalog.i18nc(
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "Model Checker",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a monitor stage in Cura.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -54,7 +54,7 @@ Item
|
||||
UM.ActiveTool.setProperty("MeshType", type)
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "uranium"}
|
||||
UM.I18nCatalog { id: catalog; name: "cura"}
|
||||
|
||||
Column
|
||||
{
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides the Per Model Settings.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "Post Processing",
|
||||
"author": "Ultimaker",
|
||||
"version": "2.2.1",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"description": "Extension that allows for user created scripts for post processing",
|
||||
"catalog": "cura"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a prepare stage in Cura.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a preview stage in Cura.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides the Simulation view.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Submits anonymous slice info. Can be disabled through preferences.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ class SolidView(View):
|
||||
theme = Application.getInstance().getTheme()
|
||||
self._xray_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb()))
|
||||
self._xray_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
|
||||
self._xray_composite_shader.setUniformValue("u_flat_error_color_mix", 0.) # Don't show flat error color in solid-view.
|
||||
|
||||
renderer = self.getRenderer()
|
||||
if not self._composite_pass or not 'xray' in self._composite_pass.getLayerBindings():
|
||||
@ -267,13 +268,23 @@ class SolidView(View):
|
||||
Class that ducktypes to be a Numpy ndarray.
|
||||
"""
|
||||
def __init__(self, qimage):
|
||||
self.__array_interface__ = {
|
||||
"shape": (qimage.height(), qimage.width()),
|
||||
"typestr": "|u4", # Use 4 bytes per pixel rather than 3, since Numpy doesn't support 3.
|
||||
"data": (int(qimage.bits()), False),
|
||||
"strides": (qimage.bytesPerLine(), 3), # This does the magic: For each line, skip the correct number of bytes. Bytes per pixel is always 3 due to QImage.Format.Format_RGB888.
|
||||
"version": 3
|
||||
}
|
||||
bits_pointer = qimage.bits()
|
||||
if bits_pointer is None: # If this happens before there is a window.
|
||||
self.__array_interface__ = {
|
||||
"shape": (0, 0),
|
||||
"typestr": "|u4",
|
||||
"data": (0, False),
|
||||
"strides": (1, 3),
|
||||
"version": 3
|
||||
}
|
||||
else:
|
||||
self.__array_interface__ = {
|
||||
"shape": (qimage.height(), qimage.width()),
|
||||
"typestr": "|u4", # Use 4 bytes per pixel rather than 3, since Numpy doesn't support 3.
|
||||
"data": (int(bits_pointer), False),
|
||||
"strides": (qimage.bytesPerLine(), 3), # This does the magic: For each line, skip the correct number of bytes. Bytes per pixel is always 3 due to QImage.Format.Format_RGB888.
|
||||
"version": 3
|
||||
}
|
||||
array = np.asarray(QImageArrayView(xray_img)).view(np.dtype({
|
||||
"r": (np.uint8, 0, "red"),
|
||||
"g": (np.uint8, 1, "green"),
|
||||
@ -286,25 +297,6 @@ class SolidView(View):
|
||||
Logger.log("i", "X-Ray overlay found non-manifold pixels.")
|
||||
|
||||
def event(self, event):
|
||||
if event.type == Event.ViewActivateEvent:
|
||||
# FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching.
|
||||
# This can happen when you do the following steps:
|
||||
# 1. Start Cura
|
||||
# 2. Load a model
|
||||
# 3. Switch to Custom mode
|
||||
# 4. Select the model and click on the per-object tool icon
|
||||
# 5. Switch view to Layer view or X-Ray
|
||||
# 6. Cura will very likely crash
|
||||
# It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why.
|
||||
# This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL
|
||||
# context is None.
|
||||
if Platform.isOSX():
|
||||
if QOpenGLContext.currentContext() is None:
|
||||
Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later")
|
||||
Application.getInstance().callLater(lambda e = event: self.event(e))
|
||||
return
|
||||
|
||||
|
||||
if event.type == Event.ViewDeactivateEvent:
|
||||
if self._composite_pass and 'xray' in self._composite_pass.getLayerBindings():
|
||||
self.getRenderer().removeRenderPass(self._xray_pass)
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides a normal solid mesh view.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
@ -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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
"name": "Toolbox",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"description": "Find, manage and install new Cura packages."
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-122.4 196.9 50.3 74.1"
|
||||
enable-background="new -122.4 196.9 50.3 74.1" xml:space="preserve">
|
||||
<title>logo</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="homepage" sketch:type="MSPage">
|
||||
<g id="Home-menu" transform="translate(-35.000000, -37.000000)" sketch:type="MSArtboardGroup">
|
||||
<g id="hero-3" transform="translate(-792.000000, -68.000000)" sketch:type="MSLayerGroup">
|
||||
<g id="Group-2">
|
||||
</g>
|
||||
</g>
|
||||
<g id="Menu" sketch:type="MSLayerGroup">
|
||||
<g id="logo" transform="translate(35.000000, 37.845203)" sketch:type="MSShapeGroup">
|
||||
<g id="Robot" transform="translate(51.265823, 0.000000)">
|
||||
<path id="Fill-23" fill="#000000" d="M-139.9,203.6c-0.3,0-0.6,0-0.9,0.1c-0.3,0.1-0.5,0.2-0.7,0.4c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c-0.1,0.1-0.2,0.2-0.3,0.3c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.1,0.4
|
||||
c0,0,0,0,0,0c0,0.1,0,0.3,0,0.4c0,0.3,0.1,0.6,0.2,0.9c0.1,0.3,0.3,0.5,0.5,0.7c0.2,0.2,0.5,0.4,0.7,0.5
|
||||
c0.3,0.1,0.6,0.2,0.9,0.2c0.1,0,0.2,0,0.3,0c0,0,0.1,0,0.1,0c0.1,0,0.1,0,0.2,0c0,0,0.1,0,0.1,0c0.1,0,0.1,0,0.2-0.1
|
||||
c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0,0c0.3-0.1,0.5-0.2,0.7-0.4c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.2-0.3,0.2-0.4
|
||||
c0.1-0.3,0.2-0.5,0.2-0.8s-0.1-0.6-0.2-0.9c-0.1-0.2-0.2-0.3-0.3-0.5c0,0,0,0,0,0c-0.1-0.1-0.1-0.1-0.2-0.2
|
||||
c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.2-0.4-0.2C-139.3,203.7-139.6,203.6-139.9,203.6"/>
|
||||
<path id="Fill-24" fill="#000000" d="M-138.4,211.3c-0.1-0.1-0.1-0.1-0.2-0.1c-0.1,0-0.2-0.1-0.3-0.1l-11.4-0.3
|
||||
c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0.1-0.2,0.1c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.1,0.1-0.1,0.2c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0.1-0.1,0.1-0.1,0.2v0.7c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.2,0.2c0.1,0.1,0.1,0.1,0.2,0.1c0.1,0,0.2,0.1,0.3,0.1
|
||||
l11.4,0.2c0.1,0,0.2,0,0.3,0c0.1,0,0.1-0.1,0.2-0.1c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.2,0.1-0.2v-0.6c0-0.1,0-0.2-0.1-0.2
|
||||
C-138.3,211.5-138.4,211.4-138.4,211.3"/>
|
||||
<path id="Fill-25" fill="#000000" d="M-151,207.2c0.2,0.2,0.5,0.4,0.9,0.6c0.3,0.1,0.7,0.2,1,0.2c0.1,0,0.2,0,0.4,0
|
||||
c0,0,0.1,0,0.1,0c0.1,0,0.2,0,0.3-0.1c0,0,0,0,0,0c0.1,0,0.2,0,0.2-0.1c0,0,0,0,0,0c0.1,0,0.3-0.1,0.4-0.2c0,0,0,0,0,0
|
||||
c0.1-0.1,0.3-0.2,0.4-0.3c0.2-0.2,0.4-0.5,0.6-0.8c0.1-0.3,0.2-0.6,0.2-0.9c0-0.3-0.1-0.7-0.2-1c-0.1-0.3-0.3-0.6-0.6-0.8
|
||||
c-0.2-0.2-0.5-0.4-0.8-0.6c-0.3-0.1-0.7-0.2-1-0.2c-0.4,0-0.7,0-1,0.1c-0.3,0.1-0.6,0.3-0.8,0.4c0,0-0.1,0-0.1,0.1
|
||||
c0,0-0.1,0.1-0.1,0.1c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.2-0.2,0.2c0,0.1-0.1,0.1-0.1,0.2c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c-0.1,0.1-0.1,0.3-0.1,0.4c0,0,0,0,0,0c0,0.1-0.1,0.3-0.1,0.5c0,0.3,0.1,0.7,0.2,1C-151.4,206.7-151.3,207-151,207.2"/>
|
||||
<path id="Fill-26" fill="#000000" d="M-139.8,235.5h0.9c0.6,0,2.1,0.3,2.1,0.3s-1.5,0.3-2.1,0.3h-0.9c0,0-0.1,0-0.1,0l-0.1,0.8
|
||||
l-0.1-0.8c-0.7-0.1-1.7-0.2-1.7-0.2s1-0.2,1.7-0.2l-0.3-1.6l-3.2-0.1l-0.2,1.9l1.9,0.1l-1.9,0.1l-0.2,1.8h0.4
|
||||
c0.9,0,3.1,0.3,3.1,0.3s-2.2,0.3-3.1,0.3h-0.4l-0.1,0.7l-0.1-0.7h-0.7c-0.9,0-3.1-0.3-3.1-0.3s2.2-0.3,3.1-0.3h0.7l-0.2-1.8
|
||||
l-4.6,0.3l-0.4,1.9l-0.4-1.9l-3.6-0.4l3.6-0.4l0.4-1.8l-5.1-0.2l9.8-0.4l-0.1-1.6l-3-0.2l-0.3,1.6l-0.3-1.6l-5.9-0.4l5.9-0.4
|
||||
l0.4-1.7l0.3,1.6l3-0.2l0.2-2.1c-0.9-0.1-1.6-0.2-1.6-0.2s0.8-0.1,1.7-0.2l0.2-1.6h-0.6c-1.1,0-3.7-0.4-3.7-0.4
|
||||
s2.6-0.4,3.7-0.4h0.7l0.1-1.2l0.1,1.2h0.6c1.1,0,3.8,0.4,3.8,0.4s-2.7,0.4-3.8,0.4h-0.5l0.2,1.5c0.4,0,0.8-0.1,1.1-0.1h1.5
|
||||
c1,0,3.7,0.4,3.7,0.4c0,0-2.6,0.4-3.7,0.4h-1.5c-0.3,0-0.7,0-1.1-0.1l0.2,2l2.5,0.7l-2.5,0.7l-0.1,1.5l3.1-0.1l0.5-3.4l0.5,3.4
|
||||
l4.2,0.5l-4.2,0.5L-139.8,235.5L-139.8,235.5L-139.8,235.5z M-150.9,227.5h1c0.7,0,2.4,0.3,2.4,0.3s-1.7,0.3-2.4,0.3h-1
|
||||
c-0.7,0-2.3-0.3-2.3-0.3S-151.6,227.5-150.9,227.5L-150.9,227.5z M-137.4,230.5h0.1c0.4,0,1.3,0.3,1.3,0.3s-0.9,0.3-1.3,0.3
|
||||
h-0.6c-0.4,0-1.3-0.3-1.3-0.3s0.9-0.3,1.3-0.3H-137.4L-137.4,230.5z M-131.8,221.6c0-0.1,0-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2
|
||||
c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.2,0-0.3,0l-25,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0.1-0.1,0.1-0.2,0.2
|
||||
c0,0.1-0.1,0.2-0.1,0.2c-0.2,1.8-0.3,3.6-0.3,5.4c-0.1,1.8-0.1,3.6-0.1,5.4c0,1.8,0,3.6,0.1,5.4c0.1,1.8,0.2,3.6,0.3,5.4
|
||||
c0,0.1,0,0.2,0.1,0.2c0,0.1,0.1,0.1,0.2,0.2c0.1,0.1,0.2,0.1,0.2,0.1c0.2,0.1,6.9,0.4,6.9,0.4c0.5-0.3,1-0.6,1.5-0.8
|
||||
c0.5-0.2,1-0.4,1.6-0.6c0.3-0.1,0.5-0.2,0.8-0.2c0,0,0,0,0,0c0.2,0,0.3-0.1,0.5-0.1c0.1,0,0.2-0.1,0.3-0.1c0,0,0,0,0,0h0
|
||||
c0,0,0,0,0.1,0c0.1,0,0.1,0,0.2,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0.1,0,0.2,0,0.3,0c0.1,0,0.1,0,0.2,0c0.3,0,0.6-0.1,0.8-0.1
|
||||
c0.3,0,0.5,0,0.8,0c0.1,0,0.1,0,0.2,0c0.2,0,0.4,0,0.6,0h0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0h0c0.1,0,0.3,0,0.4,0
|
||||
c0.1,0,0.2,0,0.3,0c2,0.2,3.8,1.1,3.8,1.1c0.5-0.1,6-1.3,6.1-1.3c0.1,0,0.2-0.1,0.2-0.1c0.1-0.1,0.1-0.1,0.2-0.2
|
||||
c0-0.1,0.1-0.1,0.1-0.2c0.1-1.7,0.2-3.3,0.3-5c0.1-1.7,0.1-3.3,0.1-5c0-1.7,0-3.3-0.1-5C-131.6,224.9-131.7,223.2-131.8,221.6
|
||||
L-131.8,221.6z"/>
|
||||
<path id="Fill-27" fill="#000000" d="M-149.4,233.6l0.4,1.8l4.5,0.3l-0.2-1.9L-149.4,233.6"/>
|
||||
<path id="Fill-28" fill="#000000" d="M-142.3,260.2c-0.2,0.3-0.2,2.2-0.1,2.6c0.1,0.5,2.1,0.1,2.1,0.1s-1.1-2.1,0.7-2.7
|
||||
c0.4-0.1,1.6-0.4,2.9-0.6c1.9-0.4,4.1-0.7,4.1-0.7s-1.1-0.3-2.7-0.1l-0.3,0l-0.3,0h0l-3.7,0.5
|
||||
C-141.4,259.6-142,259.8-142.3,260.2"/>
|
||||
<path id="Fill-29" fill="#000000" d="M-128.1,243.8v-1.5c0,0,0,0,0,0c0.1-0.1,0.1-0.1,0.2-0.2c0.1-0.1,0.3-0.2,0.4-0.3
|
||||
c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.2-0.1-0.4c0-0.1-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.3-0.3-0.3c0,0-0.1-0.1-0.1-0.1v-3.2
|
||||
c0,0,0.1,0,0.1,0c0.3-0.1,0.6-0.2,0.8-0.4c0,0.1,0.1,0.1,0.1,0.2c0.2,0.3,0.5,0.6,0.7,1.1c0.2,0.4,0.4,0.9,0.5,1.5
|
||||
C-125.8,239.5-125.2,243-128.1,243.8L-128.1,243.8z M-128.1,234.4c0.3-0.1,0.8-0.2,1.1-0.3c0.1,0,0.2-0.1,0.3-0.1v0.3
|
||||
c-0.1,0-0.2,0.1-0.2,0.1c-0.4,0.2-0.9,0.4-1.2,0.5V234.4L-128.1,234.4z M-128.1,232.8c0.3,0,0.7-0.1,1.1-0.1
|
||||
c0.1,0,0.3,0,0.4-0.1v0.3c-0.1,0-0.2,0.1-0.4,0.1c-0.4,0.1-0.8,0.2-1.1,0.3V232.8L-128.1,232.8z M-128.1,231.1
|
||||
c0.4,0,1,0,1.5,0.1v0.2c-0.5,0.1-1,0.2-1.5,0.3V231.1L-128.1,231.1z M-128.1,229.4c0.3,0.1,0.7,0.1,1.1,0.2
|
||||
c0.1,0,0.3,0.1,0.4,0.1v0.3c-0.1,0-0.3,0-0.4,0c-0.4,0-0.7,0-1,0V229.4L-128.1,229.4z M-128.1,227.6c0.4,0.1,0.8,0.2,1.2,0.4
|
||||
c0.1,0,0.2,0.1,0.3,0.1v0.3c-0.1,0-0.2-0.1-0.4-0.1c-0.4-0.1-0.8-0.1-1.1-0.2V227.6L-128.1,227.6z M-128.1,221.4
|
||||
C-128.1,221.4-128,221.4-128.1,221.4c0.5,0.3,1,1.6,1.3,2.2c0.3,0.6,0.4,1.3,0.4,2.1c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.1
|
||||
c0,0-0.1,0.1-0.1,0.1c0,0-0.1,0-0.2,0c0,0-0.1,0-0.2,0s-0.1,0-0.2,0c0,0-0.1,0-0.1,0c-0.3,0-0.6,0-0.9,0L-128.1,221.4
|
||||
L-128.1,221.4z M-129.9,221.4v22.7c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.3-0.3,0.5-0.5,0.7c-0.2,0.2-0.5,0.4-0.7,0.5
|
||||
c-0.3,0.1-0.6,0.2-0.9,0.2l-1.5,0.1c0,0,0,0,0,0l-0.1-0.3c-0.1-0.4-0.5-0.7-0.8-0.7l-1.6,0.1l-19,1.2c-0.4,0-0.8,0.3-0.9,0.7
|
||||
l-0.4,0.9c0,0,0,0,0,0l-1.3,0.1c-0.4,0-0.7,0-1-0.1c-0.3-0.1-0.6-0.2-0.9-0.4c-0.2-0.2-0.4-0.4-0.6-0.7
|
||||
c-0.1-0.3-0.2-0.6-0.2-0.9v-25c0-0.3,0.1-0.7,0.2-0.9c0.1-0.3,0.3-0.6,0.6-0.8c0.2-0.2,0.5-0.4,0.9-0.5
|
||||
c0.3-0.1,0.6-0.2,0.9-0.2c0,0,0.1,0,0.1,0l1.2,0c0-0.1,0-0.1,0-0.2l0-0.4c0-0.4,0.3-0.7,0.7-0.7l11.4,0.2l1.1,0l2.7,0l1.7,0
|
||||
l5.2,0.1c0.3,0,0.6,0.2,0.7,0.6c0,0.1,0,0.1,0,0.2l0,0.2c0,0,0,0.1,0,0.1l0.8,0c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.5
|
||||
c0.2,0.2,0.4,0.4,0.5,0.7C-130,220.7-129.9,221-129.9,221.4L-129.9,221.4L-129.9,221.4z M-137.6,255.8v-6.4c0,0,0-0.1,0-0.1
|
||||
s0,0,0-0.1c0.1,0,0.3,0,0.4,0l0,2.1c0.7-0.6,1.4-1.2,2.3-0.9c0.4,0.1,0.7,0.3,1,0.4v4.6L-137.6,255.8L-137.6,255.8z
|
||||
M-141.1,258.1l5.5-0.6l1.7-0.2c0.3,0,0.5,0,0.7,0.1c0.2,0.1,0.4,0.2,0.6,0.4c0.2,0.2,0.3,0.4,0.4,0.7c0,0,0,0.1,0,0.1
|
||||
c0.1,0.2,0.1,0.5,0.1,0.8v3c-0.4,0.1-1.1,0.1-1.8,0.3c-2.4,0.3-6.1,0.8-7.4,1c-2,0.3-1.9-0.5-1.9-0.5v-2.4c0-0.3,0-0.5,0.1-0.8
|
||||
c0-0.1,0-0.1,0.1-0.2c0.1-0.3,0.2-0.6,0.4-0.8c0.2-0.2,0.4-0.4,0.6-0.6C-141.6,258.2-141.4,258.1-141.1,258.1L-141.1,258.1z
|
||||
M-140.6,251.9c0.6,0.4,1.4,0.8,2.2,0.4l0.2-0.1l-0.4,3.7l-3.2,0.3v-6.4c0,0,0-0.1,0-0.2c0.1,0,0.2,0,0.4,0l0,3
|
||||
C-141.2,252.2-140.8,251.7-140.6,251.9L-140.6,251.9z M-131.6,262.9c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.1
|
||||
c0,0.1,0,0.1,0,0.2v0.4c0,0.1,0,0.1,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.1,0.1c0,0-0.1,0-0.1,0.1l-11.8,1.6
|
||||
c-0.1,0-0.1,0-0.2,0c0,0-0.1,0-0.1-0.1c0,0-0.1-0.1-0.1-0.1c0-0.1,0-0.1,0-0.2v-0.5c0-0.1,0-0.1,0-0.2c0-0.1,0-0.1,0.1-0.2
|
||||
c0,0,0.1-0.1,0.1-0.1c0,0,0.1-0.1,0.2-0.1l9.4-1.3l0.1,0l0.2,0l0.8-0.1l0.8-0.1L-131.6,262.9L-131.6,262.9z M-145.2,265.7
|
||||
c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.1c0,0-0.1,0.1-0.2,0.1l-12.6,1.7c-0.4,0-0.5-0.5-0.3-1l-7.5-6.2
|
||||
l8.2,5.7c0,0,0.1,0,0.3,0l11.9-1.6c0.1,0,0.1,0,0.2,0c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.1c0,0.1,0,0.1,0,0.2V265.7
|
||||
L-145.2,265.7z M-165.1,245.8c-0.1,0-0.2,0-0.2,0.1c-0.1,0-0.2,0-0.3,0.1c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.2,0h0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c-0.2,0-0.4-0.1-0.6-0.1c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.2,0c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.1,0
|
||||
c0,0,0,0,0,0c-0.1,0-0.1,0-0.2-0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0h0c-0.1,0-0.1-0.1-0.2-0.1c0,0,0,0,0,0
|
||||
c0,0-0.1-0.1-0.1-0.1c0,0,0,0,0,0l0,0c0,0-0.1,0-0.1-0.1c0,0,0,0-0.1,0c0,0,3.1-0.1,3.1-0.1s-1.3-0.8-1.6-1.1
|
||||
c-0.5-0.5,0.7-2.9,0.8-3.2l-2.4-1.1l-0.5-0.2l0.6,0.1l2.7,0.3c-0.2-0.3-1.6-0.6-2.3-0.9c-0.3-0.1-0.4-0.2-0.3-0.3c0,0,0,0,0,0
|
||||
c0.1,0,0.3,0,0.5,0.1c0.4,0,0.9,0,1.6,0c1.4,0,2.3-0.1,2.9-0.2c0,0,0,0.1,0.1,0.1c0.2,0.4,0.4,1,0.6,1.6
|
||||
c0.2,0.6,0.4,1.2,0.4,1.8c0.1,0.6,0.1,1.2-0.1,1.7h0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2-0.1-0.4-0.1c-0.1,0-0.2-0.1-0.3-0.1
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0-0.2-0.1-0.3-0.1c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.2c0-0.1,0-0.2-0.1-0.3
|
||||
c0-0.1-0.1-0.2-0.1-0.2c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.1c-0.1,0-0.1,0-0.2,0
|
||||
c-0.2,0-0.3,0.1-0.4,0.2c-0.1,0.1-0.3,0.3-0.4,0.4c-0.1,0.2-0.2,0.3-0.2,0.5c-0.1,0.2-0.1,0.3-0.1,0.4c0,0.1,0.1,0.2,0.2,0.3
|
||||
c0.1,0.1,0.3,0.2,0.4,0.3c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0,0.1,0.1,0.2,0.1c0.2,0.1,0.4,0.2,0.5,0.2c0,0.1,0,0.2,0,0.3
|
||||
c0,0.1,0,0.2,0,0.4c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.2,0.2C-165,245.7-165.1,245.8-165.1,245.8L-165.1,245.8z
|
||||
M-163.9,235.5l0,0.3c-0.1,0-0.1,0.1-0.2,0.1c-0.7,0.4-1.5,0.6-1.6,0.6c-0.1,0-0.7,0.1-1.2,0.1c-0.3,0-0.5,0-0.7,0
|
||||
c-0.3,0-0.4,0-0.5,0c-0.1,0-0.8-0.1-1.4-0.3l0-0.4c0.6,0,1.3,0,1.4,0c0,0,0.2,0,0.5,0c0.2,0,0.4,0,0.7,0c0.6,0,1.1-0.1,1.2-0.1
|
||||
c0.2,0,0.9-0.1,1.6-0.3C-164.1,235.5-164,235.5-163.9,235.5L-163.9,235.5z M-164.2,234.4c-0.7,0.2-1.4,0.4-1.5,0.4
|
||||
c-0.1,0-0.6,0.1-1.1,0.1c-0.3,0-0.6,0-0.8,0c-0.2,0-0.3,0-0.3,0c-0.1,0-0.8-0.1-1.5-0.3c0,0-0.1,0-0.1,0l0-0.3c0,0,0.1,0,0.1,0
|
||||
c0.7,0,1.4-0.1,1.5-0.1c0,0,0.2,0,0.3,0c0.2,0,0.5,0,0.8,0c0.5,0,1-0.1,1.1-0.1c0.1,0,0.8,0,1.5,0c0.1,0,0.2,0,0.3,0l0,0.3
|
||||
C-164,234.3-164.1,234.4-164.2,234.4L-164.2,234.4z M-169.7,232.6c0.7-0.1,1.7-0.3,1.9-0.3l0.1,0l2,0c0.2,0,1.2,0.1,1.9,0.2
|
||||
v0.2c-0.7,0.1-1.7,0.3-1.9,0.3l-2,0l-0.1,0c-0.2,0-1.2-0.1-1.9-0.2V232.6L-169.7,232.6z M-163.8,231v0.3c-0.1,0-0.2,0-0.4,0
|
||||
c-0.7,0-1.5,0-1.6,0c-0.1,0-0.6,0-1.2,0c-0.3,0-0.5,0-0.7,0c-0.2,0-0.4,0-0.5,0c-0.2,0-0.9-0.1-1.6,0v-0.4
|
||||
c0.7-0.2,1.4-0.3,1.6-0.4c0,0,0.2,0,0.5,0c0.2,0,0.5,0,0.7,0c0.6,0,1.1,0,1.2,0c0.2,0,0.9,0.1,1.6,0.4
|
||||
C-164,230.9-163.9,230.9-163.8,231L-163.8,231z M-164.1,229.6c-0.7-0.2-1.6-0.2-1.7-0.2c-0.1,0-0.7-0.1-1.3-0.1
|
||||
c-0.2,0-0.4,0-0.6,0c-0.4,0-0.6,0-0.7,0l-0.6,0c-0.2,0-0.5,0-0.7,0.1V229c0.2-0.1,0.5-0.1,0.7-0.2c0.3-0.1,0.6-0.1,0.7-0.1
|
||||
c0.1,0,0.3,0,0.7,0c0.2,0,0.4,0,0.6,0c0.6,0,1.2,0.1,1.3,0.1c0.2,0,1,0.2,1.7,0.5c0.1,0,0.2,0.1,0.3,0.1v0.2
|
||||
C-163.9,229.6-164,229.6-164.1,229.6L-164.1,229.6z M-170.2,226.5c0-0.1,0-0.4,0-0.4c0.2-1.6,4.1-6,4.1-6s1.7-0.5,2.4-0.5
|
||||
c0.5,0,1,0.4,0.9,1.6c0,0.2-0.4,5-0.4,5c0,0,0,0.1-0.1,0.1c0,0-0.1,0.1-0.1,0.1c-0.7,0.6-5.9,0.6-6.4,0.5
|
||||
C-170.1,226.9-170.2,226.7-170.2,226.5L-170.2,226.5z M-151.5,257.2l0-6.5c0,0,0.1,0,0.4,0l0,2.2c0.7-0.6,1.5-1.2,2.4-1
|
||||
c0.5,0.1,0.9,0.3,1.3,0.6v4.4L-151.5,257.2L-151.5,257.2z M-154.7,253.4c0.6,0.5,1.4,0.9,2.3,0.4l0.4-0.2l-0.4,3.7l-3.5,0.4
|
||||
v-5.6l0-1c0,0,0.2,0,0.5,0l0,3.1C-155.4,253.6-155,253.2-154.7,253.4L-154.7,253.4z M-158.1,262.2c0,0,0-0.3,0.1-0.4
|
||||
c0-0.1,0-0.2,0.1-0.3c0.1-0.3,0.3-0.6,0.5-0.8c0.2-0.2,0.4-0.5,0.7-0.6c0.3-0.2,0.5-0.3,0.8-0.3l7.9-0.9c0.3,0,0.6,0,0.8,0.1
|
||||
c0.2,0.1,0.5,0.2,0.7,0.4c0.2,0.2,0.3,0.4,0.4,0.7c0,0.1,0,0.1,0.1,0.2c0.1,0.2,0.1,0.5,0.1,0.7v3.2l-0.4,0l-10.1,1.3l-0.5,0.1
|
||||
c0,0-1,0.1-1.2-0.6v-1.7c-0.4-0.3-6.4-5.2-6.4-5.2L-158.1,262.2L-158.1,262.2z M-158.8,206.5l1.2-0.1l0.5,0c0,0,0,0,0.1,0
|
||||
c0,0,0,0,0.1,0c0.1,0,0.2,0,0.3,0.1c0.6,0.4,1,1.7,1,3.4c0,2-0.6,3.6-1.3,3.6v0h0c0,0,0,0,0,0h0c0,0,0,0,0,0h0l-1.3,0l-1.6,0.1
|
||||
l-0.3,0c0,0,0,0,0.1,0c0.4-0.1,0.7-0.8,0.9-1.8c0.1-0.5,0.1-1.1,0.1-1.7c0-0.7-0.1-1.3-0.2-1.8c-0.2-1-0.6-1.6-0.9-1.6
|
||||
L-158.8,206.5L-158.8,206.5z M-153.3,196.9c0.4-0.1,0.9-0.1,0.9,0.3c0,0.4,0,1.3,0,1.8h0c-0.3,0.1-0.6,0.3-0.9,0.4V196.9
|
||||
L-153.3,196.9z M-151.5,200.9c0.3-0.1,0.7-0.2,1-0.1l8.4,0.5l2.8,0.2l0.6,0c0.3,0,0.7,0.1,1,0.2c0.3,0.1,0.6,0.3,0.8,0.5
|
||||
c0.2,0.2,0.4,0.5,0.5,0.8c0.1,0.3,0.2,0.6,0.2,0.9v10.7c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.3-0.3,0.5-0.5,0.7l-3.9-0.1l-11.7-0.2
|
||||
c-0.2-0.2-0.3-0.4-0.4-0.6c-0.1-0.3-0.2-0.6-0.2-1v-11.2c0-0.3,0.1-0.7,0.2-0.9c0.1-0.3,0.3-0.5,0.6-0.7
|
||||
C-152.2,201.2-151.9,201-151.5,200.9L-151.5,200.9z M-139.3,197c0.4-0.1,0.8-0.1,0.8,0.3c0,0.5,0,1.6,0,2.1c0,0,0,0,0,0l-0.8,0
|
||||
V197L-139.3,197z M-172.7,226.4c-0.2,1.8,0.9,2.2,0.9,2.2v5.8c0,0.1,0,0.1,0,0.2l0,1.2c0,0,0,0.1,0,0.1v0.7
|
||||
c0,0-0.1,0.1-0.1,0.1c-0.4,0.4-0.7,0.9-1,1.4c-0.4,0.7-0.6,1.3-0.7,2c-0.1,0.9-0.1,1.7,0.2,2.5c0.1,0.3,0.3,0.7,0.5,1
|
||||
c0.9,1.5,2,2.5,3,3.2c1.8,1.3,5,1,5,1v6.7c-0.1,0.1-0.1,0.1-0.2,0.2c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.3-0.5,0.5
|
||||
c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.5-0.4,0.7c0,0.1-0.1,0.1-0.1,0.2c-0.1,0.2-0.2,0.4-0.2,0.6c0,0.1,0,0.2-0.1,0.3
|
||||
c0,0.1,0,0.1,0,0.2c0,0.2,0,0.4,0,0.6v2.1c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.4-0.4,0.7c0,0,0,0.1,0,0.1
|
||||
c-0.1,0.3-0.1,0.5-0.1,0.8v0.4c0,0.1,0,0.3,0,0.4c0,0.1,0.1,0.3,0.1,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.3,0.3,0.4
|
||||
l6.1,5.5c0,0,0,0,0.1,0.1c0.3,0.2,0.6,0.4,0.9,0.5c0.3,0.1,0.7,0.1,1,0.1c0.1,0,0.2,0,0.3,0l12.9-1.8c0.4,0,0.7-0.2,1-0.3
|
||||
c0.4,0.1,0.7,0.1,1.1,0l11.8-1.6c0.4-0.1,0.8-0.2,1.2-0.4c0.3-0.2,0.5-0.4,0.7-0.7c0.2-0.2,0.3-0.5,0.4-0.8
|
||||
c0.1-0.3,0.1-0.5,0.1-0.8v-0.4c0-0.3-0.1-0.6-0.2-0.8c-0.1-0.3-0.3-0.5-0.5-0.8c0,0,0,0-0.1-0.1v-2.4c0-1.4-0.4-3.3-2.2-3.8
|
||||
v-6.8c0.6-0.1,3.7-0.4,3.7-2.7c0.9-0.1,3.4-0.6,4.4-4.2c0.2-0.8,0.3-1.7,0.2-2.6c-0.1-0.7-0.3-1.4-0.6-2
|
||||
c-0.2-0.5-0.5-1-0.9-1.4c-0.1-0.1-0.1-0.1-0.2-0.2v-8.3c0.2-0.1,0.8-0.4,0.8-1.7c0-0.8-1.9-5.4-2.5-6.1c-0.7-0.8-2-1.8-2.8-2.1
|
||||
c-0.2-0.1-0.4-0.2-0.5-0.2c-0.6-0.2-1.2-0.3-1.8-0.3c-0.1-0.3-0.4-0.5-0.7-0.5l-1.1,0v-2.6c1.2-0.5,1.9-1.7,1.9-3.3
|
||||
c0-0.7-0.1-2.5-1.2-3.4c-0.2-0.2-0.4-0.3-0.6-0.4v-2.5l0-0.2c0-0.6-0.1-1.1-0.3-1.6c-0.2-0.5-0.5-1-0.9-1.4
|
||||
c-0.4-0.4-0.9-0.8-1.4-1c-0.3-0.2-0.7-0.3-1-0.3V197c0-0.5-0.9-1.3-3.8-0.3c0,0,0,0,0,0c-0.4,0.1-0.6,0.5-0.6,0.8v1.7l-8.2-0.5
|
||||
c-0.4,0-0.8,0-1.2,0.1v-1.9c0-0.5-0.9-1.3-3.8-0.3c-0.4,0.1-0.7,0.5-0.7,0.8v3.9c0,0.1,0,0.3,0,0.3l-3.8,3.1
|
||||
c-0.4,0-0.9,0.1-1.5,0.2c-1.9,0.4-2.6,3.1-2.6,5.3c0,2,0.5,3.5,1.3,4.4v2.3l-3.8,1.1C-167.3,218.3-172.5,224.6-172.7,226.4
|
||||
L-172.7,226.4z"/>
|
||||
<path id="Fill-30" fill="#000000" d="M-157,265.1c0.5,0,4.2-0.5,4.2-0.5s-0.8-0.2-0.5-1.6c0.2-1,2.1-1.4,3.4-1.7
|
||||
c1.3-0.3,3.1-0.5,3.1-0.5s-1.1-0.3-2.7-0.1l-0.3,0l-0.3,0h0c0,0-5.7,0.7-7.1,1.3c-0.4,0.2-0.4,1.9-0.4,2.7
|
||||
C-157.6,265.1-157.3,265.1-157,265.1"/>
|
||||
<path id="Fill-31" fill="#000000" d="M-156,260.4c-0.6,0.1-1,0.9-1,0.9l5-0.7C-152.1,260.5-153.5,260.1-156,260.4"/>
|
||||
<path id="Fill-32" fill="#000000" d="M-138.6,258.8c0,0-1.4-0.4-2.4-0.3c-0.9,0.1-1.2,1-1.2,1
|
||||
C-141.7,259-138.7,258.8-138.6,258.8"/>
|
||||
<path id="Fill-33" fill="#000000" d="M-165.5,226.1c0,0-1.8-1-1.5-1.6c0.5-0.9,1.9-0.6,2.4-1.2c0.8-1,0.6-2.5,0.6-2.5
|
||||
s-1.7-0.3-2.2,0.3c-0.8,1-3,4.2-3.2,4.6C-170.2,226.7-165.5,226.1-165.5,226.1"/>
|
||||
<path id="Fill-34" fill="#000000" d="M-157,212.9c0.2,0,0.5-0.6,0.7-1.7c0,0-0.1,0-0.1,0c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.1-0.1-0.2-0.1c-0.2-0.2-0.2-0.4-0.4-0.6c-0.3-0.2-0.6-0.2-0.9-0.1c0,0-0.3,0.1-0.4-0.2c0,0.2,0,0.3,0,0.5
|
||||
c0,0.5-0.1,1.1-0.2,1.4c-0.1,0.3-0.1,0.6-0.2,0.8c0,0.1-0.1,0.2-0.1,0.3L-157,212.9C-157.1,212.9-157.1,212.9-157,212.9"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 17 KiB |
@ -16,19 +16,10 @@ Column
|
||||
height: childrenRect.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image
|
||||
{
|
||||
id: profileImage
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "../../images/logobot.svg"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: Math.round(parent.width / 4)
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: welcomeTextLabel
|
||||
text: catalog.i18nc("@description", "Get plugins and materials verified by Ultimaker")
|
||||
text: catalog.i18nc("@description", "Please sign in to get verified plugins and materials for Ultimaker Cura Enterprise")
|
||||
width: Math.round(parent.width / 2)
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
@ -3,5 +3,5 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading model files.",
|
||||
"api": "7.1.0"
|
||||
"api": "7.2.0"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides support for reading Ultimaker Format Packages.",
|
||||
"supported_sdk_versions": ["7.1.0"],
|
||||
"supported_sdk_versions": ["7.2.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.1",
|
||||
"api": "7.2.0",
|
||||
"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.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -1,26 +1,29 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Logger import Logger # To log errors talking to the API.
|
||||
from UM.Message import Message
|
||||
from UM.Signal import Signal
|
||||
from cura.API import Account
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
from .CloudApiClient import CloudApiClient
|
||||
from .CloudOutputDevice import CloudOutputDevice
|
||||
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||
|
||||
|
||||
## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
|
||||
# Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code.
|
||||
# API spec is available on https://api.ultimaker.com/docs/connect/spec/.
|
||||
class CloudOutputDeviceManager:
|
||||
"""The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
|
||||
|
||||
Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code.
|
||||
API spec is available on https://api.ultimaker.com/docs/connect/spec/.
|
||||
"""
|
||||
|
||||
META_CLUSTER_ID = "um_cloud_cluster_id"
|
||||
META_NETWORK_KEY = "um_network_key"
|
||||
@ -44,14 +47,16 @@ class CloudOutputDeviceManager:
|
||||
# Create a timer to update the remote cluster list
|
||||
self._update_timer = QTimer()
|
||||
self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000))
|
||||
self._update_timer.setSingleShot(False)
|
||||
# The timer is restarted explicitly after an update was processed. This prevents 2 concurrent updates
|
||||
self._update_timer.setSingleShot(True)
|
||||
self._update_timer.timeout.connect(self._getRemoteClusters)
|
||||
|
||||
# Ensure we don't start twice.
|
||||
self._running = False
|
||||
|
||||
## Starts running the cloud output device manager, thus periodically requesting cloud data.
|
||||
def start(self):
|
||||
"""Starts running the cloud output device manager, thus periodically requesting cloud data."""
|
||||
|
||||
if self._running:
|
||||
return
|
||||
if not self._account.isLoggedIn:
|
||||
@ -61,8 +66,9 @@ class CloudOutputDeviceManager:
|
||||
self._update_timer.start()
|
||||
self._getRemoteClusters()
|
||||
|
||||
## Stops running the cloud output device manager.
|
||||
def stop(self):
|
||||
"""Stops running the cloud output device manager."""
|
||||
|
||||
if not self._running:
|
||||
return
|
||||
self._running = False
|
||||
@ -70,87 +76,164 @@ class CloudOutputDeviceManager:
|
||||
self._update_timer.stop()
|
||||
self._onGetRemoteClustersFinished([]) # Make sure we remove all cloud output devices.
|
||||
|
||||
## Force refreshing connections.
|
||||
def refreshConnections(self) -> None:
|
||||
"""Force refreshing connections."""
|
||||
|
||||
self._connectToActiveMachine()
|
||||
|
||||
## Called when the uses logs in or out
|
||||
def _onLoginStateChanged(self, is_logged_in: bool) -> None:
|
||||
"""Called when the uses logs in or out"""
|
||||
|
||||
if is_logged_in:
|
||||
self.start()
|
||||
else:
|
||||
self.stop()
|
||||
|
||||
## Gets all remote clusters from the API.
|
||||
def _getRemoteClusters(self) -> None:
|
||||
"""Gets all remote clusters from the API."""
|
||||
|
||||
self._api.getClusters(self._onGetRemoteClustersFinished)
|
||||
|
||||
## Callback for when the request for getting the clusters is finished.
|
||||
def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None:
|
||||
"""Callback for when the request for getting the clusters is finished."""
|
||||
|
||||
new_clusters = []
|
||||
online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse]
|
||||
|
||||
for device_id, cluster_data in online_clusters.items():
|
||||
if device_id not in self._remote_clusters:
|
||||
self._onDeviceDiscovered(cluster_data)
|
||||
else:
|
||||
self._onDiscoveredDeviceUpdated(cluster_data)
|
||||
new_clusters.append(cluster_data)
|
||||
|
||||
self._onDevicesDiscovered(new_clusters)
|
||||
|
||||
# Inform whether new cloud printers have been detected. If they have, the welcome wizard can close.
|
||||
self._account._new_cloud_printers_detected = len(new_clusters) > 0
|
||||
self._account.cloudPrintersDetectedChanged.emit(len(new_clusters) > 0)
|
||||
|
||||
removed_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys())
|
||||
for device_id in removed_device_keys:
|
||||
self._onDiscoveredDeviceRemoved(device_id)
|
||||
|
||||
def _onDeviceDiscovered(self, cluster_data: CloudClusterResponse) -> None:
|
||||
device = CloudOutputDevice(self._api, cluster_data)
|
||||
CuraApplication.getInstance().getDiscoveredPrintersModel().addDiscoveredPrinter(
|
||||
ip_address=device.key,
|
||||
key=device.getId(),
|
||||
name=device.getName(),
|
||||
create_callback=self._createMachineFromDiscoveredDevice,
|
||||
machine_type=device.printerType,
|
||||
device=device
|
||||
)
|
||||
self._remote_clusters[device.getId()] = device
|
||||
self.discoveredDevicesChanged.emit()
|
||||
self._connectToActiveMachine()
|
||||
if new_clusters or removed_device_keys:
|
||||
self.discoveredDevicesChanged.emit()
|
||||
if removed_device_keys:
|
||||
# If the removed device was active we should connect to the new active device
|
||||
self._connectToActiveMachine()
|
||||
# Schedule a new update
|
||||
self._update_timer.start()
|
||||
|
||||
def _onDiscoveredDeviceUpdated(self, cluster_data: CloudClusterResponse) -> None:
|
||||
device = self._remote_clusters.get(cluster_data.cluster_id)
|
||||
if not device:
|
||||
def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None:
|
||||
"""**Synchronously** create machines for discovered devices
|
||||
|
||||
Any new machines are made available to the user.
|
||||
May take a long time to complete. As this code needs access to the Application
|
||||
and blocks the GIL, creating a Job for this would not make sense.
|
||||
Shows a Message informing the user of progress.
|
||||
"""
|
||||
new_devices = []
|
||||
remote_clusters_added = False
|
||||
for cluster_data in clusters:
|
||||
device = CloudOutputDevice(self._api, cluster_data)
|
||||
# Create a machine if we don't already have it. Do not make it the active machine.
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
# We only need to add it if it wasn't already added by "local" network or by cloud.
|
||||
if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
|
||||
and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key.
|
||||
new_devices.append(device)
|
||||
elif device.getId() not in self._remote_clusters:
|
||||
self._remote_clusters[device.getId()] = device
|
||||
remote_clusters_added = True
|
||||
|
||||
if not new_devices:
|
||||
if remote_clusters_added:
|
||||
self._connectToActiveMachine()
|
||||
return
|
||||
CuraApplication.getInstance().getDiscoveredPrintersModel().updateDiscoveredPrinter(
|
||||
ip_address=device.key,
|
||||
name=cluster_data.friendly_name,
|
||||
machine_type=device.printerType
|
||||
|
||||
new_devices.sort(key = lambda x: x.name.lower())
|
||||
|
||||
image_path = os.path.join(
|
||||
CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting") or "",
|
||||
"resources", "svg", "cloud-flow-completed.svg"
|
||||
)
|
||||
self.discoveredDevicesChanged.emit()
|
||||
|
||||
message = Message(
|
||||
title = self.I18N_CATALOG.i18ncp(
|
||||
"info:status",
|
||||
"New printer detected from your Ultimaker account",
|
||||
"New printers detected from your Ultimaker account",
|
||||
len(new_devices)
|
||||
),
|
||||
progress = 0,
|
||||
lifetime = 0,
|
||||
image_source = image_path
|
||||
)
|
||||
message.show()
|
||||
|
||||
for idx, device in enumerate(new_devices):
|
||||
message_text = self.I18N_CATALOG.i18nc(
|
||||
"info:status", "Adding printer {} ({}) from your account",
|
||||
device.name,
|
||||
device.printerTypeName
|
||||
)
|
||||
message.setText(message_text)
|
||||
if len(new_devices) > 1:
|
||||
message.setProgress((idx / len(new_devices)) * 100)
|
||||
CuraApplication.getInstance().processEvents()
|
||||
self._remote_clusters[device.getId()] = device
|
||||
|
||||
# If there is no active machine, activate the first available cloud printer
|
||||
activate = not CuraApplication.getInstance().getMachineManager().activeMachine
|
||||
self._createMachineFromDiscoveredDevice(device.getId(), activate = activate)
|
||||
|
||||
message.setProgress(None)
|
||||
|
||||
max_disp_devices = 3
|
||||
if len(new_devices) > max_disp_devices:
|
||||
num_hidden = len(new_devices) - max_disp_devices + 1
|
||||
device_name_list = ["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]]
|
||||
device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "- and {} others", num_hidden))
|
||||
device_names = "\n".join(device_name_list)
|
||||
else:
|
||||
device_names = "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices])
|
||||
|
||||
message_text = self.I18N_CATALOG.i18nc(
|
||||
"info:status",
|
||||
"Cloud printers added from your account:\n{}",
|
||||
device_names
|
||||
)
|
||||
message.setText(message_text)
|
||||
|
||||
def _onDiscoveredDeviceRemoved(self, device_id: str) -> None:
|
||||
device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice]
|
||||
if not device:
|
||||
return
|
||||
device.close()
|
||||
CuraApplication.getInstance().getDiscoveredPrintersModel().removeDiscoveredPrinter(device.key)
|
||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
if device.key in output_device_manager.getOutputDeviceIds():
|
||||
output_device_manager.removeOutputDevice(device.key)
|
||||
self.discoveredDevicesChanged.emit()
|
||||
|
||||
def _createMachineFromDiscoveredDevice(self, key: str) -> None:
|
||||
def _createMachineFromDiscoveredDevice(self, key: str, activate: bool = True) -> None:
|
||||
device = self._remote_clusters[key]
|
||||
if not device:
|
||||
return
|
||||
|
||||
# Create a new machine and activate it.
|
||||
# Create a new machine.
|
||||
# We do not use use MachineManager.addMachine here because we need to set the cluster ID before activating it.
|
||||
new_machine = CuraStackBuilder.createMachine(device.name, device.printerType)
|
||||
if not new_machine:
|
||||
Logger.log("e", "Failed creating a new machine")
|
||||
return
|
||||
new_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
|
||||
CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId())
|
||||
self._connectToOutputDevice(device, new_machine)
|
||||
|
||||
## Callback for when the active machine was changed by the user or a new remote cluster was found.
|
||||
self._setOutputDeviceMetadata(device, new_machine)
|
||||
|
||||
if activate:
|
||||
CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId())
|
||||
|
||||
def _connectToActiveMachine(self) -> None:
|
||||
"""Callback for when the active machine was changed by the user"""
|
||||
|
||||
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not active_machine:
|
||||
return
|
||||
@ -169,13 +252,17 @@ class CloudOutputDeviceManager:
|
||||
# Remove device if it is not meant for the active machine.
|
||||
output_device_manager.removeOutputDevice(device.key)
|
||||
|
||||
## Connects to an output device and makes sure it is registered in the output device manager.
|
||||
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:
|
||||
def _setOutputDeviceMetadata(self, device: CloudOutputDevice, machine: GlobalStack):
|
||||
machine.setName(device.name)
|
||||
machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
|
||||
machine.setMetaDataEntry("group_name", device.name)
|
||||
machine.addConfiguredConnectionType(device.connectionType.value)
|
||||
|
||||
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:
|
||||
"""Connects to an output device and makes sure it is registered in the output device manager."""
|
||||
|
||||
self._setOutputDeviceMetadata(device, machine)
|
||||
|
||||
if not device.isConnected():
|
||||
device.connect()
|
||||
|
||||
|
@ -125,7 +125,10 @@ class ToolPathUploader:
|
||||
if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES:
|
||||
self._retries += 1
|
||||
Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString())
|
||||
self._uploadChunk()
|
||||
try:
|
||||
self._uploadChunk()
|
||||
except ValueError: # Asynchronously it could have completed in the meanwhile.
|
||||
pass
|
||||
return
|
||||
|
||||
# Http codes that are not to be retried are assumed to be errors.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import List, Optional, Union, Dict, Any
|
||||
|
||||
@ -44,6 +44,7 @@ class ClusterPrintJobStatus(BaseModel):
|
||||
# \param compatible_machine_families: Family names of machines suitable for this print job
|
||||
# \param impediments_to_printing: A list of reasons that prevent this job from being printed on the associated
|
||||
# printer
|
||||
# \param preview_url: URL to the preview image (same as wou;d've been included in the ufp).
|
||||
def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str,
|
||||
time_total: int, uuid: str,
|
||||
configuration: List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]],
|
||||
@ -57,6 +58,7 @@ class ClusterPrintJobStatus(BaseModel):
|
||||
build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None,
|
||||
compatible_machine_families: List[str] = None,
|
||||
impediments_to_printing: List[Union[Dict[str, Any], ClusterPrintJobImpediment]] = None,
|
||||
preview_url: Optional[str] = None,
|
||||
**kwargs) -> None:
|
||||
self.assigned_to = assigned_to
|
||||
self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration)
|
||||
@ -76,6 +78,7 @@ class ClusterPrintJobStatus(BaseModel):
|
||||
self.uuid = uuid
|
||||
self.deleted_at = deleted_at
|
||||
self.printed_on_uuid = printed_on_uuid
|
||||
self.preview_url = preview_url
|
||||
|
||||
self.configuration_changes_required = self.parseModels(ClusterPrintJobConfigurationChange,
|
||||
configuration_changes_required) \
|
||||
|
@ -1,10 +1,13 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtGui import QImage
|
||||
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
|
||||
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
||||
|
||||
@ -32,3 +35,13 @@ class UM3PrintJobOutputModel(PrintJobOutputModel):
|
||||
image = QImage()
|
||||
image.loadFromData(data)
|
||||
self.updatePreviewImage(image)
|
||||
|
||||
def loadPreviewImageFromUrl(self, url: str) -> None:
|
||||
HttpRequestManager.getInstance().get(url=url, callback=self._onImageLoaded, error_callback=self._onImageLoaded)
|
||||
|
||||
def _onImageLoaded(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None:
|
||||
if not HttpRequestManager.replyIndicatesSuccess(reply, error):
|
||||
Logger.warning("Requesting preview image failed, response code {0} while trying to connect to {1}".format(
|
||||
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url()))
|
||||
return
|
||||
self.updatePreviewImageData(reply.readAll())
|
||||
|
@ -92,9 +92,13 @@ class SendMaterialJob(Job):
|
||||
parts = []
|
||||
|
||||
# Add the material file.
|
||||
with open(file_path, "rb") as f:
|
||||
parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\""
|
||||
.format(file_name = file_name), f.read()))
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\""
|
||||
.format(file_name = file_name), f.read()))
|
||||
except FileNotFoundError:
|
||||
Logger.error("Unable to send material {material_id}, since it has been deleted in the meanwhile.".format(material_id = material_id))
|
||||
return
|
||||
|
||||
# Add the material signature file if needed.
|
||||
signature_file_path = "{}.sig".format(file_path)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import os
|
||||
from time import time
|
||||
@ -52,7 +52,6 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
super().__init__(device_id=device_id, address=address, properties=properties, connection_type=connection_type,
|
||||
parent=parent)
|
||||
|
||||
# Trigger the printersChanged signal when the private signal is triggered.
|
||||
self.printersChanged.connect(self._clusterPrintersChanged)
|
||||
|
||||
@ -330,6 +329,8 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice):
|
||||
self._updateAssignedPrinter(model, remote_job.printer_uuid)
|
||||
if remote_job.assigned_to:
|
||||
self._updateAssignedPrinter(model, remote_job.assigned_to)
|
||||
if remote_job.preview_url:
|
||||
model.loadPreviewImageFromUrl(remote_job.preview_url)
|
||||
return model
|
||||
|
||||
## Updates the printer assignment for the given print job model.
|
||||
|
@ -191,7 +191,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
try:
|
||||
self._serial = Serial(str(self._serial_port), self._baud_rate, timeout=self._timeout, writeTimeout=self._timeout)
|
||||
except SerialException:
|
||||
Logger.log("w", "An exception occurred while trying to create serial connection")
|
||||
Logger.warning("An exception occurred while trying to create serial connection.")
|
||||
return
|
||||
except OSError as e:
|
||||
Logger.warning("The serial device is suddenly unavailable while trying to create a serial connection: {err}".format(err = str(e)))
|
||||
return
|
||||
CuraApplication.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "USB printing",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.2",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -236,23 +236,11 @@ _variant_translations_materials = {
|
||||
}
|
||||
} # type: Dict[str, Dict[str, str]]
|
||||
|
||||
|
||||
## Converts configuration from Cura 2.1's file formats to Cura 2.2's.
|
||||
#
|
||||
# It converts the machine instances and profiles.
|
||||
class VersionUpgrade21to22(VersionUpgrade):
|
||||
## Gets the version number from a config file.
|
||||
#
|
||||
# In all config files that concern this version upgrade, the version
|
||||
# number is stored in general/version, so get the data from that key.
|
||||
#
|
||||
# \param serialised The contents of a config file.
|
||||
# \return The version number of that config file.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Gets the fallback quality to use for a specific machine-variant-material
|
||||
# combination.
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 2.1 to Cura 2.2.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 2.2 to Cura 2.4.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ _split_settings = { #These settings should be copied to all settings it was spli
|
||||
"support_interface_line_distance": {"support_roof_line_distance", "support_bottom_line_distance"}
|
||||
} # type: Dict[str, Set[str]]
|
||||
|
||||
|
||||
## A collection of functions that convert the configuration of the user in Cura
|
||||
# 2.5 to a configuration for Cura 2.6.
|
||||
#
|
||||
@ -28,24 +29,6 @@ class VersionUpgrade25to26(VersionUpgrade):
|
||||
super().__init__()
|
||||
self._current_fdm_printer_count = 2
|
||||
|
||||
## Gets the version number from a CFG file in Uranium's 2.5 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 2.5 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades the preferences file from version 2.5 to 2.6.
|
||||
#
|
||||
# \param serialised The serialised form of a preferences file.
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 2.5 to Cura 2.6.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -63,74 +63,6 @@ setting_version = -3
|
||||
}
|
||||
]
|
||||
|
||||
## Tests the technique that gets the version number from CFG files.
|
||||
#
|
||||
# \param data The parametrised data to test with. It contains a test name
|
||||
# to debug with, the serialised contents of a CFG file and the correct
|
||||
# version number in that CFG file.
|
||||
# \param upgrader The instance of the upgrade class to test.
|
||||
@pytest.mark.parametrize("data", test_cfg_version_good_data)
|
||||
def test_cfgVersionGood(data, upgrader):
|
||||
version = upgrader.getCfgVersion(data["file_data"])
|
||||
assert version == data["version"]
|
||||
|
||||
test_cfg_version_bad_data = [
|
||||
{
|
||||
"test_name": "Empty",
|
||||
"file_data": "",
|
||||
"exception": configparser.Error #Explicitly not specified further which specific error we're getting, because that depends on the implementation of configparser.
|
||||
},
|
||||
{
|
||||
"test_name": "No General",
|
||||
"file_data": """[values]
|
||||
layer_height = 0.1337
|
||||
""",
|
||||
"exception": configparser.Error
|
||||
},
|
||||
{
|
||||
"test_name": "No Version",
|
||||
"file_data": """[general]
|
||||
true = false
|
||||
""",
|
||||
"exception": configparser.Error
|
||||
},
|
||||
{
|
||||
"test_name": "Not a Number",
|
||||
"file_data": """[general]
|
||||
version = not-a-text-version-number
|
||||
""",
|
||||
"exception": ValueError
|
||||
},
|
||||
{
|
||||
"test_name": "Setting Value NaN",
|
||||
"file_data": """[general]
|
||||
version = 4
|
||||
[metadata]
|
||||
setting_version = latest_or_something
|
||||
""",
|
||||
"exception": ValueError
|
||||
},
|
||||
{
|
||||
"test_name": "Major-Minor",
|
||||
"file_data": """[general]
|
||||
version = 1.2
|
||||
""",
|
||||
"exception": ValueError
|
||||
}
|
||||
]
|
||||
|
||||
## Tests whether getting a version number from bad CFG files gives an
|
||||
# exception.
|
||||
#
|
||||
# \param data The parametrised data to test with. It contains a test name
|
||||
# to debug with, the serialised contents of a CFG file and the class of
|
||||
# exception it needs to throw.
|
||||
# \param upgrader The instance of the upgrader to test.
|
||||
@pytest.mark.parametrize("data", test_cfg_version_bad_data)
|
||||
def test_cfgVersionBad(data, upgrader):
|
||||
with pytest.raises(data["exception"]):
|
||||
upgrader.getCfgVersion(data["file_data"])
|
||||
|
||||
test_upgrade_preferences_removed_settings_data = [
|
||||
{
|
||||
"test_name": "Removed Setting",
|
||||
|
@ -64,29 +64,12 @@ _renamed_quality_profiles = {
|
||||
"um3_bb0.8_TPU_Not_Supported_Superdraft_Quality": "um3_bb0.8_TPU_Superdraft_Print",
|
||||
} # type: Dict[str, str]
|
||||
|
||||
|
||||
## A collection of functions that convert the configuration of the user in Cura
|
||||
# 2.6 to a configuration for Cura 2.7.
|
||||
#
|
||||
# All of these methods are essentially stateless.
|
||||
class VersionUpgrade26to27(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 2.6 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 2.6 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades a preferences file from version 2.6 to 2.7.
|
||||
#
|
||||
# \param serialised The serialised form of a preferences file.
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 2.6 to Cura 2.7.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -35,13 +35,6 @@ infill_sparse_density = 42
|
||||
""",
|
||||
"version": 3000000
|
||||
},
|
||||
{
|
||||
"test_name": "Negative Version", #Why not?
|
||||
"file_data": """[general]
|
||||
version = -20
|
||||
""",
|
||||
"version": -20000000
|
||||
},
|
||||
{
|
||||
"test_name": "Setting Version",
|
||||
"file_data": """[general]
|
||||
@ -50,15 +43,6 @@ version = 1
|
||||
setting_version = 1
|
||||
""",
|
||||
"version": 1000001
|
||||
},
|
||||
{
|
||||
"test_name": "Negative Setting Version",
|
||||
"file_data": """[general]
|
||||
version = 1
|
||||
[metadata]
|
||||
setting_version = -3
|
||||
""",
|
||||
"version": 999997
|
||||
}
|
||||
]
|
||||
|
||||
@ -73,62 +57,6 @@ def test_cfgVersionGood(data, upgrader):
|
||||
version = upgrader.getCfgVersion(data["file_data"])
|
||||
assert version == data["version"]
|
||||
|
||||
test_cfg_version_bad_data = [
|
||||
{
|
||||
"test_name": "Empty",
|
||||
"file_data": "",
|
||||
"exception": configparser.Error #Explicitly not specified further which specific error we're getting, because that depends on the implementation of configparser.
|
||||
},
|
||||
{
|
||||
"test_name": "No General",
|
||||
"file_data": """[values]
|
||||
layer_height = 0.1337
|
||||
""",
|
||||
"exception": configparser.Error
|
||||
},
|
||||
{
|
||||
"test_name": "No Version",
|
||||
"file_data": """[general]
|
||||
true = false
|
||||
""",
|
||||
"exception": configparser.Error
|
||||
},
|
||||
{
|
||||
"test_name": "Not a Number",
|
||||
"file_data": """[general]
|
||||
version = not-a-text-version-number
|
||||
""",
|
||||
"exception": ValueError
|
||||
},
|
||||
{
|
||||
"test_name": "Setting Value NaN",
|
||||
"file_data": """[general]
|
||||
version = 4
|
||||
[metadata]
|
||||
setting_version = latest_or_something
|
||||
""",
|
||||
"exception": ValueError
|
||||
},
|
||||
{
|
||||
"test_name": "Major-Minor",
|
||||
"file_data": """[general]
|
||||
version = 1.2
|
||||
""",
|
||||
"exception": ValueError
|
||||
}
|
||||
]
|
||||
|
||||
## Tests whether getting a version number from bad CFG files gives an
|
||||
# exception.
|
||||
#
|
||||
# \param data The parametrised data to test with. It contains a test name
|
||||
# to debug with, the serialised contents of a CFG file and the class of
|
||||
# exception it needs to throw.
|
||||
# \param upgrader The instance of the upgrader to test.
|
||||
@pytest.mark.parametrize("data", test_cfg_version_bad_data)
|
||||
def test_cfgVersionBad(data, upgrader):
|
||||
with pytest.raises(data["exception"]):
|
||||
upgrader.getCfgVersion(data["file_data"])
|
||||
|
||||
test_upgrade_stacks_with_not_supported_data = [
|
||||
{
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 2.7 to Cura 3.0.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -62,24 +62,6 @@ _RENAMED_DEFINITION_DICT = {
|
||||
|
||||
|
||||
class VersionUpgrade30to31(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 3.0 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.0 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades a preferences file from version 3.0 to 3.1.
|
||||
#
|
||||
# \param serialised The serialised form of a preferences file.
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 3.0 to Cura 3.1.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -68,24 +68,6 @@ _RENAMED_QUALITY_TYPES = {
|
||||
class VersionUpgrade32to33(VersionUpgrade):
|
||||
temporary_group_name_counter = 1
|
||||
|
||||
## Gets the version number from a CFG file in Uranium's 3.2 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.2 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades a preferences file from version 3.2 to 3.3.
|
||||
#
|
||||
# \param serialised The serialised form of a preferences file.
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -14,24 +14,6 @@ _renamed_settings = {
|
||||
## Upgrades configurations from the state they were in at version 3.3 to the
|
||||
# state they should be in at version 3.4.
|
||||
class VersionUpgrade33to34(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 3.3 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.3 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades instance containers to have the new version
|
||||
# number.
|
||||
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -63,24 +63,6 @@ _RENAMED_MATERIAL_PROFILES = {
|
||||
## Upgrades configurations from the state they were in at version 3.4 to the
|
||||
# state they should be in at version 3.5.
|
||||
class VersionUpgrade34to35(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 3.4 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.4 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades Preferences to have the new version number.
|
||||
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -34,13 +34,6 @@ class VersionUpgrade35to40(VersionUpgrade):
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades Preferences to have the new version number.
|
||||
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 3.5 to Cura 4.0.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -23,24 +23,6 @@ _renamed_quality_profiles = {
|
||||
## Upgrades configurations from the state they were in at version 4.0 to the
|
||||
# state they should be in at version 4.1.
|
||||
class VersionUpgrade40to41(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 4.0 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 4.0 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades instance containers to have the new version
|
||||
# number.
|
||||
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"description": "Upgrades configurations from Cura 4.0 to Cura 4.1.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -213,27 +213,10 @@ _creality_limited_quality_type = {
|
||||
"extra_coarse": "draft"
|
||||
}
|
||||
|
||||
|
||||
## Upgrades configurations from the state they were in at version 4.1 to the
|
||||
# state they should be in at version 4.2.
|
||||
class VersionUpgrade41to42(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 4.1 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 4.1 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades instance containers to have the new version
|
||||
# number.
|
||||
#
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 4.1 to Cura 4.2.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -56,27 +56,10 @@ _renamed_settings = {
|
||||
"support_infill_angle": "support_infill_angles"
|
||||
} # type: Dict[str, str]
|
||||
|
||||
|
||||
## Upgrades configurations from the state they were in at version 4.2 to the
|
||||
# state they should be in at version 4.3.
|
||||
class VersionUpgrade42to43(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 4.2 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 4.2 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
def upgradePreferences(self, serialized: str, filename: str):
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 4.2 to Cura 4.3.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
@ -26,12 +26,6 @@ _renamed_container_id_map = {
|
||||
|
||||
|
||||
class VersionUpgrade43to44(VersionUpgrade):
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades Preferences to have the new version number.
|
||||
#
|
||||
|
@ -3,6 +3,6 @@
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 4.3 to Cura 4.4.",
|
||||
"api": "7.1",
|
||||
"api": "7.2.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user