Merge branch 'master' of https://github.com/Ultimaker/Cura
|
@ -4,6 +4,7 @@ from typing import List
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
|
from UM.Logger import Logger
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
@ -27,10 +28,14 @@ class ArrangeObjectsJob(Job):
|
||||||
title = i18n_catalog.i18nc("@info:title", "Finding Location"))
|
title = i18n_catalog.i18nc("@info:title", "Finding Location"))
|
||||||
status_message.show()
|
status_message.show()
|
||||||
|
|
||||||
|
found_solution_for_all = None
|
||||||
|
try:
|
||||||
found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes)
|
found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes)
|
||||||
|
except: # If the thread crashes, the message should still close
|
||||||
|
Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.")
|
||||||
|
|
||||||
status_message.hide()
|
status_message.hide()
|
||||||
if not found_solution_for_all:
|
if found_solution_for_all is not None and not found_solution_for_all:
|
||||||
no_full_solution_message = Message(
|
no_full_solution_message = Message(
|
||||||
i18n_catalog.i18nc("@info:status",
|
i18n_catalog.i18nc("@info:status",
|
||||||
"Unable to find a location within the build volume for all objects"),
|
"Unable to find a location within the build volume for all objects"),
|
||||||
|
|
|
@ -3,6 +3,7 @@ from pynest2d import Point, Box, Item, NfpConfig, nest
|
||||||
from typing import List, TYPE_CHECKING, Optional, Tuple
|
from typing import List, TYPE_CHECKING, Optional, Tuple
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
from UM.Math.Matrix import Matrix
|
from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Polygon import Polygon
|
from UM.Math.Polygon import Polygon
|
||||||
from UM.Math.Quaternion import Quaternion
|
from UM.Math.Quaternion import Quaternion
|
||||||
|
@ -44,6 +45,9 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV
|
||||||
node_items = []
|
node_items = []
|
||||||
for node in nodes_to_arrange:
|
for node in nodes_to_arrange:
|
||||||
hull_polygon = node.callDecoration("getConvexHull")
|
hull_polygon = node.callDecoration("getConvexHull")
|
||||||
|
if not hull_polygon or hull_polygon.getPoints is None:
|
||||||
|
Logger.log("w", "Object {} cannot be arranged because it has no convex hull.".format(node.getName()))
|
||||||
|
continue
|
||||||
converted_points = []
|
converted_points = []
|
||||||
for point in hull_polygon.getPoints():
|
for point in hull_polygon.getPoints():
|
||||||
converted_points.append(Point(point[0] * factor, point[1] * factor))
|
converted_points.append(Point(point[0] * factor, point[1] * factor))
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright (c) 2019 Ultimaker B.V.
|
# Copyright (c) 2020 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
from hashlib import sha512
|
from hashlib import sha512
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from typing import Optional
|
from typing import Optional, Any, Dict, Tuple
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from cura.OAuth2.Models import AuthenticationResponse, UserProfile, OAuth2Settin
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationHelpers:
|
class AuthorizationHelpers:
|
||||||
"""Class containing several helpers to deal with the authorization flow."""
|
"""Class containing several helpers to deal with the authorization flow."""
|
||||||
|
|
||||||
|
@ -121,10 +122,13 @@ class AuthorizationHelpers:
|
||||||
if not user_data or not isinstance(user_data, dict):
|
if not user_data or not isinstance(user_data, dict):
|
||||||
Logger.log("w", "Could not parse user data from token: %s", user_data)
|
Logger.log("w", "Could not parse user data from token: %s", user_data)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return UserProfile(
|
return UserProfile(
|
||||||
user_id = user_data["user_id"],
|
user_id = user_data["user_id"],
|
||||||
username = user_data["username"],
|
username = user_data["username"],
|
||||||
profile_image_url = user_data.get("profile_image_url", "")
|
profile_image_url = user_data.get("profile_image_url", ""),
|
||||||
|
organization_id = user_data.get("organization", {}).get("organization_id", ""),
|
||||||
|
subscriptions = user_data.get("subscriptions", [])
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (c) 2019 Ultimaker B.V.
|
# Copyright (c) 2020 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any, List
|
||||||
|
|
||||||
|
|
||||||
class BaseModel:
|
class BaseModel:
|
||||||
|
@ -27,6 +27,8 @@ class UserProfile(BaseModel):
|
||||||
user_id = None # type: Optional[str]
|
user_id = None # type: Optional[str]
|
||||||
username = None # type: Optional[str]
|
username = None # type: Optional[str]
|
||||||
profile_image_url = None # type: Optional[str]
|
profile_image_url = None # type: Optional[str]
|
||||||
|
organization_id = None # type: Optional[str]
|
||||||
|
subscriptions = None # type: Optional[List[Dict[str, Any]]]
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationResponse(BaseModel):
|
class AuthenticationResponse(BaseModel):
|
||||||
|
|
|
@ -998,6 +998,11 @@ class MachineManager(QObject):
|
||||||
self.activeMaterialChanged.emit()
|
self.activeMaterialChanged.emit()
|
||||||
self.activeIntentChanged.emit()
|
self.activeIntentChanged.emit()
|
||||||
|
|
||||||
|
# Force an update of resolve values
|
||||||
|
property_names = ["resolve", "validationState"]
|
||||||
|
for setting_key in self._global_container_stack.getAllKeys():
|
||||||
|
self._global_container_stack.propertiesChanged.emit(setting_key, property_names)
|
||||||
|
|
||||||
def _onMaterialNameChanged(self) -> None:
|
def _onMaterialNameChanged(self) -> None:
|
||||||
self.activeMaterialChanged.emit()
|
self.activeMaterialChanged.emit()
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
class UltimakerCloudScope(DefaultUserAgentScope):
|
class UltimakerCloudScope(DefaultUserAgentScope):
|
||||||
"""Add an Authorization header to the request for Ultimaker Cloud Api requests.
|
"""
|
||||||
|
Add an Authorization header to the request for Ultimaker Cloud Api requests, if available.
|
||||||
When the user is not logged in or a token is not available, a warning will be logged
|
Also add the user agent headers (see DefaultUserAgentScope).
|
||||||
Also add the user agent headers (see DefaultUserAgentScope)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, application: CuraApplication):
|
def __init__(self, application: CuraApplication):
|
||||||
|
@ -22,7 +21,7 @@ class UltimakerCloudScope(DefaultUserAgentScope):
|
||||||
super().requestHook(request)
|
super().requestHook(request)
|
||||||
token = self._account.accessToken
|
token = self._account.accessToken
|
||||||
if not self._account.isLoggedIn or token is None:
|
if not self._account.isLoggedIn or token is None:
|
||||||
Logger.warning("Cannot add authorization to Cloud Api request")
|
Logger.debug("User is not logged in for Cloud API request to {url}".format(url = request.url().toDisplayString()))
|
||||||
return
|
return
|
||||||
|
|
||||||
header_dict = {
|
header_dict = {
|
||||||
|
|
|
@ -19,6 +19,7 @@ from UM.Scene.SceneNode import SceneNode # For typing.
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.Machines.ContainerTree import ContainerTree
|
from cura.Machines.ContainerTree import ContainerTree
|
||||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
|
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||||
|
@ -108,6 +109,7 @@ class ThreeMFReader(MeshReader):
|
||||||
|
|
||||||
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
||||||
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
||||||
|
um_node.addDecorator(ConvexHullDecorator())
|
||||||
um_node.setName(node_name)
|
um_node.setName(node_name)
|
||||||
um_node.setId(node_id)
|
um_node.setId(node_id)
|
||||||
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
||||||
|
|
|
@ -82,7 +82,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
default_engine_location = execpath
|
default_engine_location = execpath
|
||||||
break
|
break
|
||||||
|
|
||||||
self._application = CuraApplication.getInstance() #type: CuraApplication
|
application = CuraApplication.getInstance() #type: CuraApplication
|
||||||
self._multi_build_plate_model = None #type: Optional[MultiBuildPlateModel]
|
self._multi_build_plate_model = None #type: Optional[MultiBuildPlateModel]
|
||||||
self._machine_error_checker = None #type: Optional[MachineErrorChecker]
|
self._machine_error_checker = None #type: Optional[MachineErrorChecker]
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
|
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
|
||||||
|
|
||||||
default_engine_location = os.path.abspath(default_engine_location)
|
default_engine_location = os.path.abspath(default_engine_location)
|
||||||
self._application.getPreferences().addPreference("backend/location", default_engine_location)
|
application.getPreferences().addPreference("backend/location", default_engine_location)
|
||||||
|
|
||||||
# Workaround to disable layer view processing if layer view is not active.
|
# Workaround to disable layer view processing if layer view is not active.
|
||||||
self._layer_view_active = False #type: bool
|
self._layer_view_active = False #type: bool
|
||||||
|
@ -101,7 +101,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._stored_layer_data = [] # type: List[Arcus.PythonMessage]
|
self._stored_layer_data = [] # type: List[Arcus.PythonMessage]
|
||||||
self._stored_optimized_layer_data = {} # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
|
self._stored_optimized_layer_data = {} # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
|
||||||
|
|
||||||
self._scene = self._application.getController().getScene() #type: Scene
|
self._scene = application.getController().getScene() #type: Scene
|
||||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||||
|
|
||||||
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
|
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
|
||||||
|
@ -141,7 +141,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._slice_start_time = None #type: Optional[float]
|
self._slice_start_time = None #type: Optional[float]
|
||||||
self._is_disabled = False #type: bool
|
self._is_disabled = False #type: bool
|
||||||
|
|
||||||
self._application.getPreferences().addPreference("general/auto_slice", False)
|
application.getPreferences().addPreference("general/auto_slice", False)
|
||||||
|
|
||||||
self._use_timer = False #type: bool
|
self._use_timer = False #type: bool
|
||||||
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||||
|
@ -151,19 +151,20 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._change_timer.setSingleShot(True)
|
self._change_timer.setSingleShot(True)
|
||||||
self._change_timer.setInterval(500)
|
self._change_timer.setInterval(500)
|
||||||
self.determineAutoSlicing()
|
self.determineAutoSlicing()
|
||||||
self._application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||||
|
|
||||||
self._application.initializationFinished.connect(self.initialize)
|
application.initializationFinished.connect(self.initialize)
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
application = CuraApplication.getInstance()
|
||||||
|
self._multi_build_plate_model = application.getMultiBuildPlateModel()
|
||||||
|
|
||||||
self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
|
application.getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
|
||||||
if self._multi_build_plate_model:
|
if self._multi_build_plate_model:
|
||||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||||
|
|
||||||
self._application.getMachineManager().globalContainerChanged.connect(self._onGlobalStackChanged)
|
application.getMachineManager().globalContainerChanged.connect(self._onGlobalStackChanged)
|
||||||
self._onGlobalStackChanged()
|
self._onGlobalStackChanged()
|
||||||
|
|
||||||
# extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
|
# extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
|
||||||
|
@ -173,10 +174,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.backendConnected.connect(self._onBackendConnected)
|
self.backendConnected.connect(self._onBackendConnected)
|
||||||
|
|
||||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||||
self._application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||||
self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||||
|
|
||||||
self._machine_error_checker = self._application.getMachineErrorChecker()
|
self._machine_error_checker = application.getMachineErrorChecker()
|
||||||
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
|
@ -195,7 +196,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
This is useful for debugging and used to actually start the engine.
|
This is useful for debugging and used to actually start the engine.
|
||||||
:return: list of commands and args / parameters.
|
:return: list of commands and args / parameters.
|
||||||
"""
|
"""
|
||||||
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
command = [CuraApplication.getInstance().getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
||||||
parser.add_argument("--debug", action = "store_true", default = False, help = "Turn on the debug mode by setting this option.")
|
parser.add_argument("--debug", action = "store_true", default = False, help = "Turn on the debug mode by setting this option.")
|
||||||
|
@ -259,7 +260,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._scene.gcode_dict = {} #type: ignore #Because we are creating the missing attribute here.
|
self._scene.gcode_dict = {} #type: ignore #Because we are creating the missing attribute here.
|
||||||
|
|
||||||
# see if we really have to slice
|
# see if we really have to slice
|
||||||
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
application = CuraApplication.getInstance()
|
||||||
|
active_build_plate = application.getMultiBuildPlateModel().activeBuildPlate
|
||||||
build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
|
build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
|
||||||
Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
|
Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
|
||||||
num_objects = self._numObjectsPerBuildPlate()
|
num_objects = self._numObjectsPerBuildPlate()
|
||||||
|
@ -274,8 +276,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.slice()
|
self.slice()
|
||||||
return
|
return
|
||||||
self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
|
self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
|
||||||
if self._application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
|
if application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
|
||||||
self._application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
|
application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
|
||||||
|
|
||||||
if self._process is None: # type: ignore
|
if self._process is None: # type: ignore
|
||||||
self._createSocket()
|
self._createSocket()
|
||||||
|
@ -314,7 +316,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.processingProgress.emit(0)
|
self.processingProgress.emit(0)
|
||||||
Logger.log("d", "Attempting to kill the engine process")
|
Logger.log("d", "Attempting to kill the engine process")
|
||||||
|
|
||||||
if self._application.getUseExternalBackend():
|
if CuraApplication.getInstance().getUseExternalBackend():
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._process is not None: # type: ignore
|
if self._process is not None: # type: ignore
|
||||||
|
@ -350,8 +352,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.backendError.emit(job)
|
self.backendError.emit(job)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
if job.getResult() == StartJobResult.MaterialIncompatible:
|
if job.getResult() == StartJobResult.MaterialIncompatible:
|
||||||
if self._application.platformActivity:
|
if application.platformActivity:
|
||||||
self._error_message = Message(catalog.i18nc("@info:status",
|
self._error_message = Message(catalog.i18nc("@info:status",
|
||||||
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
|
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
|
@ -362,7 +365,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
return
|
return
|
||||||
|
|
||||||
if job.getResult() == StartJobResult.SettingError:
|
if job.getResult() == StartJobResult.SettingError:
|
||||||
if self._application.platformActivity:
|
if application.platformActivity:
|
||||||
if not self._global_container_stack:
|
if not self._global_container_stack:
|
||||||
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
|
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
|
||||||
return
|
return
|
||||||
|
@ -394,7 +397,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
elif job.getResult() == StartJobResult.ObjectSettingError:
|
elif job.getResult() == StartJobResult.ObjectSettingError:
|
||||||
errors = {}
|
errors = {}
|
||||||
for node in DepthFirstIterator(self._application.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(application.getController().getScene().getRoot()):
|
||||||
stack = node.callDecoration("getStack")
|
stack = node.callDecoration("getStack")
|
||||||
if not stack:
|
if not stack:
|
||||||
continue
|
continue
|
||||||
|
@ -415,7 +418,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
return
|
return
|
||||||
|
|
||||||
if job.getResult() == StartJobResult.BuildPlateError:
|
if job.getResult() == StartJobResult.BuildPlateError:
|
||||||
if self._application.platformActivity:
|
if application.platformActivity:
|
||||||
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."),
|
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."),
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
|
@ -433,7 +436,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
return
|
return
|
||||||
|
|
||||||
if job.getResult() == StartJobResult.NothingToSlice:
|
if job.getResult() == StartJobResult.NothingToSlice:
|
||||||
if self._application.platformActivity:
|
if application.platformActivity:
|
||||||
self._error_message = Message(catalog.i18nc("@info:status", "Please review settings and check if your models:"
|
self._error_message = Message(catalog.i18nc("@info:status", "Please review settings and check if your models:"
|
||||||
"\n- Fit within the build volume"
|
"\n- Fit within the build volume"
|
||||||
"\n- Are assigned to an enabled extruder"
|
"\n- Are assigned to an enabled extruder"
|
||||||
|
@ -466,7 +469,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
enable_timer = True
|
enable_timer = True
|
||||||
self._is_disabled = False
|
self._is_disabled = False
|
||||||
|
|
||||||
if not self._application.getPreferences().getValue("general/auto_slice"):
|
if not CuraApplication.getInstance().getPreferences().getValue("general/auto_slice"):
|
||||||
enable_timer = False
|
enable_timer = False
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if node.callDecoration("isBlockSlicing"):
|
if node.callDecoration("isBlockSlicing"):
|
||||||
|
@ -560,7 +563,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
:param error: The exception that occurred.
|
:param error: The exception that occurred.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._application.isShuttingDown():
|
if CuraApplication.getInstance().isShuttingDown():
|
||||||
return
|
return
|
||||||
|
|
||||||
super()._onSocketError(error)
|
super()._onSocketError(error)
|
||||||
|
@ -600,7 +603,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
cast(SceneNode, node.getParent()).removeChild(node)
|
cast(SceneNode, node.getParent()).removeChild(node)
|
||||||
|
|
||||||
def markSliceAll(self) -> None:
|
def markSliceAll(self) -> None:
|
||||||
for build_plate_number in range(self._application.getMultiBuildPlateModel().maxBuildPlate + 1):
|
for build_plate_number in range(CuraApplication.getInstance().getMultiBuildPlateModel().maxBuildPlate + 1):
|
||||||
if build_plate_number not in self._build_plates_to_be_sliced:
|
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||||
|
|
||||||
|
@ -696,12 +699,13 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically.
|
gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically.
|
||||||
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
||||||
gcode_list = []
|
gcode_list = []
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
for index, line in enumerate(gcode_list):
|
for index, line in enumerate(gcode_list):
|
||||||
replaced = line.replace("{print_time}", str(self._application.getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
|
replaced = line.replace("{print_time}", str(application.getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
|
||||||
replaced = replaced.replace("{filament_amount}", str(self._application.getPrintInformation().materialLengths))
|
replaced = replaced.replace("{filament_amount}", str(application.getPrintInformation().materialLengths))
|
||||||
replaced = replaced.replace("{filament_weight}", str(self._application.getPrintInformation().materialWeights))
|
replaced = replaced.replace("{filament_weight}", str(application.getPrintInformation().materialWeights))
|
||||||
replaced = replaced.replace("{filament_cost}", str(self._application.getPrintInformation().materialCosts))
|
replaced = replaced.replace("{filament_cost}", str(application.getPrintInformation().materialCosts))
|
||||||
replaced = replaced.replace("{jobname}", str(self._application.getPrintInformation().jobName))
|
replaced = replaced.replace("{jobname}", str(application.getPrintInformation().jobName))
|
||||||
|
|
||||||
gcode_list[index] = replaced
|
gcode_list[index] = replaced
|
||||||
|
|
||||||
|
@ -711,7 +715,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
Logger.log("d", "Number of models per buildplate: %s", dict(self._numObjectsPerBuildPlate()))
|
Logger.log("d", "Number of models per buildplate: %s", dict(self._numObjectsPerBuildPlate()))
|
||||||
|
|
||||||
# See if we need to process the sliced layers job.
|
# See if we need to process the sliced layers job.
|
||||||
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = application.getMultiBuildPlateModel().activeBuildPlate
|
||||||
if (
|
if (
|
||||||
self._layer_view_active and
|
self._layer_view_active and
|
||||||
(self._process_layers_job is None or not self._process_layers_job.isRunning()) and
|
(self._process_layers_job is None or not self._process_layers_job.isRunning()) and
|
||||||
|
@ -870,9 +874,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
def _onActiveViewChanged(self) -> None:
|
def _onActiveViewChanged(self) -> None:
|
||||||
"""Called when the user changes the active view mode."""
|
"""Called when the user changes the active view mode."""
|
||||||
|
|
||||||
view = self._application.getController().getActiveView()
|
view = CuraApplication.getInstance().getController().getActiveView()
|
||||||
if view:
|
if view:
|
||||||
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||||
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||||
self._layer_view_active = True
|
self._layer_view_active = True
|
||||||
# There is data and we're not slicing at the moment
|
# There is data and we're not slicing at the moment
|
||||||
|
@ -909,7 +913,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
extruder.propertyChanged.disconnect(self._onSettingChanged)
|
extruder.propertyChanged.disconnect(self._onSettingChanged)
|
||||||
extruder.containersChanged.disconnect(self._onChanged)
|
extruder.containersChanged.disconnect(self._onChanged)
|
||||||
|
|
||||||
self._global_container_stack = self._application.getMachineManager().activeMachine
|
self._global_container_stack = CuraApplication.getInstance().getMachineManager().activeMachine
|
||||||
|
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
|
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# tries to create PyQt objects on a non-main thread.
|
# tries to create PyQt objects on a non-main thread.
|
||||||
import Arcus # @UnusedImport
|
import Arcus # @UnusedImport
|
||||||
import Savitar # @UnusedImport
|
import Savitar # @UnusedImport
|
||||||
|
import pynest2d # @UnusedImport
|
||||||
|
|
||||||
from . import PostProcessingPlugin
|
from . import PostProcessingPlugin
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from ..PostProcessingPlugin import PostProcessingPlugin
|
||||||
# not sure if needed
|
# not sure if needed
|
||||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||||
|
|
||||||
""" In this file, commnunity refers to regular Cura for makers."""
|
""" In this file, community refers to regular Cura for makers."""
|
||||||
|
|
||||||
mock_plugin_registry = MagicMock()
|
mock_plugin_registry = MagicMock()
|
||||||
mock_plugin_registry.getPluginPath = MagicMock(return_value = "mocked_plugin_path")
|
mock_plugin_registry.getPluginPath = MagicMock(return_value = "mocked_plugin_path")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2020 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -116,6 +116,7 @@ class SliceInfo(QObject, Extension):
|
||||||
|
|
||||||
machine_manager = self._application.getMachineManager()
|
machine_manager = self._application.getMachineManager()
|
||||||
print_information = self._application.getPrintInformation()
|
print_information = self._application.getPrintInformation()
|
||||||
|
user_profile = self._application.getCuraAPI().account.userProfile
|
||||||
|
|
||||||
global_stack = machine_manager.activeMachine
|
global_stack = machine_manager.activeMachine
|
||||||
|
|
||||||
|
@ -124,6 +125,8 @@ class SliceInfo(QObject, Extension):
|
||||||
data["schema_version"] = 0
|
data["schema_version"] = 0
|
||||||
data["cura_version"] = self._application.getVersion()
|
data["cura_version"] = self._application.getVersion()
|
||||||
data["cura_build_type"] = ApplicationMetadata.CuraBuildType
|
data["cura_build_type"] = ApplicationMetadata.CuraBuildType
|
||||||
|
data["organization_id"] = user_profile.get("organization_id", None) if user_profile else None
|
||||||
|
data["subscriptions"] = user_profile.get("subscriptions", []) if user_profile else []
|
||||||
|
|
||||||
active_mode = self._application.getPreferences().getValue("cura/active_mode")
|
active_mode = self._application.getPreferences().getValue("cura/active_mode")
|
||||||
if active_mode == 0:
|
if active_mode == 0:
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<b>Cura Version:</b> 4.0<br/>
|
<b>Cura Version:</b> 4.8<br/>
|
||||||
<b>Operating System:</b> Windows 10<br/>
|
<b>Operating System:</b> Windows 10<br/>
|
||||||
<b>Language:</b> en_US<br/>
|
<b>Language:</b> en_US<br/>
|
||||||
<b>Machine Type:</b> Ultimaker S5<br/>
|
<b>Machine Type:</b> Ultimaker S5<br/>
|
||||||
<b>Intent Profile:</b> Default<br/>
|
<b>Intent Profile:</b> Default<br/>
|
||||||
<b>Quality Profile:</b> Fast<br/>
|
<b>Quality Profile:</b> Fast<br/>
|
||||||
<b>Using Custom Settings:</b> No
|
<b>Using Custom Settings:</b> No<br/>
|
||||||
|
<b>Organization ID (if any):</b> ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=<br/>
|
||||||
|
<b>Subscriptions (if any):</b>
|
||||||
|
<ul>
|
||||||
|
<li><b>Level:</b> 10, <b>Type:</b> Enterprise, <b>Plan:</b> Basic</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3>Extruder 1:</h3>
|
<h3>Extruder 1:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -42,8 +42,7 @@ UM.Dialog
|
||||||
Row {
|
Row {
|
||||||
id: packageRow
|
id: packageRow
|
||||||
|
|
||||||
anchors.left: parent.left
|
Layout.fillWidth: true
|
||||||
anchors.right: parent.right
|
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
spacing: UM.Theme.getSize("default_margin").width
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
leftPadding: UM.Theme.getSize("narrow_margin").width
|
leftPadding: UM.Theme.getSize("narrow_margin").width
|
||||||
|
|
|
@ -140,8 +140,7 @@ class CloudPackageChecker(QObject):
|
||||||
sync_message = Message(self._i18n_catalog.i18nc(
|
sync_message = Message(self._i18n_catalog.i18nc(
|
||||||
"@info:generic",
|
"@info:generic",
|
||||||
"Do you want to sync material and software packages with your account?"),
|
"Do you want to sync material and software packages with your account?"),
|
||||||
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ),
|
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
|
||||||
lifetime = 0)
|
|
||||||
sync_message.addAction("sync",
|
sync_message.addAction("sync",
|
||||||
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
|
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
|
||||||
icon = "",
|
icon = "",
|
||||||
|
|
Before Width: | Height: | Size: 1,014 KiB After Width: | Height: | Size: 899 KiB |
Before Width: | Height: | Size: 774 KiB After Width: | Height: | Size: 682 KiB |
Before Width: | Height: | Size: 473 KiB After Width: | Height: | Size: 430 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.2 MiB |
|
@ -81,6 +81,8 @@ class SendMaterialJob(Job):
|
||||||
container_registry = CuraApplication.getInstance().getContainerRegistry()
|
container_registry = CuraApplication.getInstance().getContainerRegistry()
|
||||||
all_materials = container_registry.findInstanceContainersMetadata(type = "material")
|
all_materials = container_registry.findInstanceContainersMetadata(type = "material")
|
||||||
all_base_files = {material["base_file"] for material in all_materials if "base_file" in material} # Filters out uniques by making it a set. Don't include files without base file (i.e. empty material).
|
all_base_files = {material["base_file"] for material in all_materials if "base_file" in material} # Filters out uniques by making it a set. Don't include files without base file (i.e. empty material).
|
||||||
|
if "empty_material" in all_base_files:
|
||||||
|
all_base_files.remove("empty_material") # Don't send the empty material.
|
||||||
|
|
||||||
for root_material_id in all_base_files:
|
for root_material_id in all_base_files:
|
||||||
if root_material_id not in materials_to_send:
|
if root_material_id not in materials_to_send:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
# Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import Arcus and Savitar first!
|
# Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import custom Sip bindings first!
|
||||||
import Savitar # Dont remove this line
|
import Savitar # Dont remove this line
|
||||||
import Arcus # No really. Don't. It needs to be there!
|
import Arcus # No really. Don't. It needs to be there!
|
||||||
|
import pynest2d # Really!
|
||||||
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -231,7 +231,7 @@ Item
|
||||||
target: enabledCheckbox
|
target: enabledCheckbox
|
||||||
property: "checked"
|
property: "checked"
|
||||||
value: Cura.MachineManager.activeStack.isEnabled
|
value: Cura.MachineManager.activeStack.isEnabled
|
||||||
when: Cura.MachineManger.activeStack != null
|
when: Cura.MachineManager.activeStack != null
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use a MouseArea to process the click on this checkbox.
|
/* Use a MouseArea to process the click on this checkbox.
|
||||||
|
|
|
@ -1,3 +1,57 @@
|
||||||
|
[4.8.0]
|
||||||
|
|
||||||
|
* (NOTE: Draft release notes for Beta, these may change for final.)
|
||||||
|
|
||||||
|
* New arrange algorithm!
|
||||||
|
Shoutout to Prusa, since they made the libnest2d library for this, and allowed a licence change.
|
||||||
|
|
||||||
|
* When opening a project file, pick any matching printer in addition to just exact match and new definition.
|
||||||
|
Previously, when someone sent you a project, you either had to have the exact same printer under the exact same name, or create an entirely new instance. Now, in the open project dialog, you can specify any printer that has a(n exactly) matching printer-type.
|
||||||
|
|
||||||
|
* Show warning message on profiles that where successfully imported, but not supported by the currently active configuration.
|
||||||
|
People where a bit confused when adding profiles, which then didn't show up. With this new version, when you add a profile that isn't supported by the current instance (but otherwise correctly imported), you get a warning-message.
|
||||||
|
|
||||||
|
* Show parts of the model below the buildplate in a different color.
|
||||||
|
When viewing the buildplate from below, there's now shadow visible anymore. As this helped the user determine what part of the model was below the buildplate, we decided to color that part differently instead.
|
||||||
|
|
||||||
|
* Show the familiar striped pattern for objects outside of the build-volume in Preview mode as well.
|
||||||
|
Models outside of the build-volume can of course not be sliced. In the Prepare mode, this was already visible with solid objects indicated in the familiar grey-yellow striped pattern. Now you can also see the objects that are still in the scene just outside if the build-volume in Preview mode.
|
||||||
|
|
||||||
|
* Iron the top-most bottom layer when spiralizing a solid model, contributed by smartavionics
|
||||||
|
Ironing was only used for top-layers, or every layer. But what is the biggest flat surface in a vase? This helpful pull request made it so that, in this case, the top-most bottom layer is used to iron on.
|
||||||
|
|
||||||
|
* Allow scrolling through setting-tooltips, useful for some plugins.
|
||||||
|
Certain plugins, such as the very useful Settings Guide, occasionally have very large tooltips. This update allows you to scroll through those.
|
||||||
|
|
||||||
|
* Bug Fixes
|
||||||
|
- Fix the simplify algorithm. Again.
|
||||||
|
- Fix percentage text-fields when scaling non-uniformly.
|
||||||
|
- Fix cloud printer stuck in connect/disconnect loop.
|
||||||
|
- Fix rare crash when processing stair stepping in support.
|
||||||
|
- Fix sudden increase in tree support branch diameter.
|
||||||
|
- Fix cases of tree-support resting against vertical wall.
|
||||||
|
- Fix conical support missing on printers with 'origin at center' set.
|
||||||
|
- Fix infill multiplier and connected lines settings not cooperating with each other.
|
||||||
|
- Fixed an issue with skin-edge support, contributed by smartavionics
|
||||||
|
- Fix printer renaming didn't always stick after restart.
|
||||||
|
- Fix move after retraction not changing speed if it's a factor 60 greater.
|
||||||
|
- Fix Windows file alteration detection (reload file popup message appears again).
|
||||||
|
- OBJ-file reader now doesn't get confused by legal negative indices.
|
||||||
|
- Fix off-by-one error that could cause horizontal faces to shift one layer upwards.
|
||||||
|
- Fix out of bounds array and lost checks for segments ended with mesh vertices, contributed bt skarasov
|
||||||
|
- Remove redundant 'successful responses' variable, contributed by aerotog
|
||||||
|
|
||||||
|
* Printer definitions and profiles
|
||||||
|
- Artillery Sidewinder X1, Artillery Sidewinder Genius, contributed by cataclism
|
||||||
|
- AnyCubic Kossel, contributed by FoxExe
|
||||||
|
- BIQU B1, contributed by looxonline
|
||||||
|
- BLV mgn Cube 300, contributed by wolfgangmauer
|
||||||
|
- Cocoon Create, Cocoon Create Touch, contributed by thushan
|
||||||
|
- Creality CR-6 SE, contributed by MatthieuMH
|
||||||
|
- Flying Bear Ghost 5, contributed by oducceu
|
||||||
|
- Fused Form 3D (FF300, FF600, FF600+, FFmini), contributed by FusedForm
|
||||||
|
- Add Acetate profiles for Strateo3D, contributed by KOUBeMT
|
||||||
|
|
||||||
[4.7.1]
|
[4.7.1]
|
||||||
For an overview of the new features in Cura 4.7, please see this video: <a href="https://www.youtube.com/watch?v=vuKuil0dJqE">Change log overview</a>
|
For an overview of the new features in Cura 4.7, please see this video: <a href="https://www.youtube.com/watch?v=vuKuil0dJqE">Change log overview</a>
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
# Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import Arcus and Savitar first!
|
# Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import custom Sip bindings first!
|
||||||
import Savitar # Dont remove this line
|
import Savitar # Dont remove this line
|
||||||
import Arcus # No really. Don't. It needs to be there!
|
import Arcus # No really. Don't. It needs to be there!
|
||||||
|
import pynest2d # Really!
|
||||||
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
||||||
# Even though your IDE says these files are not used, don't believe it. It's lying. They need to be there.
|
# Even though your IDE says these files are not used, don't believe it. It's lying. They need to be there.
|
||||||
|
|
||||||
|
|