Merge branch 'master' into fix_filtering_output_mimetypes

This commit is contained in:
Kostas Karmas 2020-10-30 15:31:04 +01:00
commit 0c28ad3223
458 changed files with 12982 additions and 4956 deletions

View file

@ -19,6 +19,7 @@ from UM.Scene.SceneNode import SceneNode # For typing.
from cura.CuraApplication import CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
@ -108,6 +109,10 @@ class ThreeMFReader(MeshReader):
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
try:
um_node.addDecorator(ConvexHullDecorator())
except:
pass
um_node.setName(node_name)
um_node.setId(node_id)
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())

View file

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

View file

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

View file

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

View file

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

View file

@ -82,7 +82,7 @@ class CuraEngineBackend(QObject, Backend):
default_engine_location = execpath
break
self._application = CuraApplication.getInstance() #type: CuraApplication
application = CuraApplication.getInstance() #type: CuraApplication
self._multi_build_plate_model = None #type: Optional[MultiBuildPlateModel]
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)
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.
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_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)
# 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._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
# 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.setInterval(500)
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:
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:
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
self._application.getMachineManager().globalContainerChanged.connect(self._onGlobalStackChanged)
application.getMachineManager().globalContainerChanged.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
# 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)
# 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)
self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
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)
def close(self) -> None:
@ -195,7 +196,7 @@ class CuraEngineBackend(QObject, Backend):
This is useful for debugging and used to actually start the engine.
: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.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.
# 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)
Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
num_objects = self._numObjectsPerBuildPlate()
@ -274,8 +276,8 @@ class CuraEngineBackend(QObject, Backend):
self.slice()
return
self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
if self._application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
self._application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
if application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
if self._process is None: # type: ignore
self._createSocket()
@ -314,7 +316,7 @@ class CuraEngineBackend(QObject, Backend):
self.processingProgress.emit(0)
Logger.log("d", "Attempting to kill the engine process")
if self._application.getUseExternalBackend():
if CuraApplication.getInstance().getUseExternalBackend():
return
if self._process is not None: # type: ignore
@ -350,8 +352,9 @@ class CuraEngineBackend(QObject, Backend):
self.backendError.emit(job)
return
application = CuraApplication.getInstance()
if job.getResult() == StartJobResult.MaterialIncompatible:
if self._application.platformActivity:
if application.platformActivity:
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"))
self._error_message.show()
@ -362,7 +365,7 @@ class CuraEngineBackend(QObject, Backend):
return
if job.getResult() == StartJobResult.SettingError:
if self._application.platformActivity:
if application.platformActivity:
if not self._global_container_stack:
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
return
@ -394,7 +397,7 @@ class CuraEngineBackend(QObject, Backend):
elif job.getResult() == StartJobResult.ObjectSettingError:
errors = {}
for node in DepthFirstIterator(self._application.getController().getScene().getRoot()):
for node in DepthFirstIterator(application.getController().getScene().getRoot()):
stack = node.callDecoration("getStack")
if not stack:
continue
@ -415,7 +418,7 @@ class CuraEngineBackend(QObject, Backend):
return
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."),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
@ -433,7 +436,7 @@ class CuraEngineBackend(QObject, Backend):
return
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:"
"\n- Fit within the build volume"
"\n- Are assigned to an enabled extruder"
@ -466,7 +469,7 @@ class CuraEngineBackend(QObject, Backend):
enable_timer = True
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
for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("isBlockSlicing"):
@ -560,7 +563,7 @@ class CuraEngineBackend(QObject, Backend):
:param error: The exception that occurred.
"""
if self._application.isShuttingDown():
if CuraApplication.getInstance().isShuttingDown():
return
super()._onSocketError(error)
@ -600,7 +603,7 @@ class CuraEngineBackend(QObject, Backend):
cast(SceneNode, node.getParent()).removeChild(node)
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:
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.
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
gcode_list = []
application = CuraApplication.getInstance()
for index, line in enumerate(gcode_list):
replaced = line.replace("{print_time}", str(self._application.getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
replaced = replaced.replace("{filament_amount}", str(self._application.getPrintInformation().materialLengths))
replaced = replaced.replace("{filament_weight}", str(self._application.getPrintInformation().materialWeights))
replaced = replaced.replace("{filament_cost}", str(self._application.getPrintInformation().materialCosts))
replaced = replaced.replace("{jobname}", str(self._application.getPrintInformation().jobName))
replaced = line.replace("{print_time}", str(application.getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
replaced = replaced.replace("{filament_amount}", str(application.getPrintInformation().materialLengths))
replaced = replaced.replace("{filament_weight}", str(application.getPrintInformation().materialWeights))
replaced = replaced.replace("{filament_cost}", str(application.getPrintInformation().materialCosts))
replaced = replaced.replace("{jobname}", str(application.getPrintInformation().jobName))
gcode_list[index] = replaced
@ -711,7 +715,7 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("d", "Number of models per buildplate: %s", dict(self._numObjectsPerBuildPlate()))
# See if we need to process the sliced layers job.
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
active_build_plate = application.getMultiBuildPlateModel().activeBuildPlate
if (
self._layer_view_active 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:
"""Called when the user changes the active view mode."""
view = self._application.getController().getActiveView()
view = CuraApplication.getInstance().getController().getActiveView()
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.
self._layer_view_active = True
# 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.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:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.

View file

@ -205,10 +205,6 @@ class StartSliceJob(Job):
for node in OneAtATimeIterator(self._scene.getRoot()):
temp_list = []
# Node can't be printed, so don't bother sending it.
if getattr(node, "_outside_buildarea", False):
continue
# Filter on current build plate
build_plate_number = node.callDecoration("getBuildPlateNumber")
if build_plate_number is not None and build_plate_number != self._build_plate_number:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -99,7 +99,7 @@ Rectangle
visible: isNetworkConfigured && !isConnected
text: catalog.i18nc("@info", "Please make sure your printer has a connection:\n- Check if the printer is turned on.\n- Check if the printer is connected to the network.\n- Check if you are signed in to discover cloud-connected printers.")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap
lineHeight: UM.Theme.getSize("monitor_text_line_large").height
lineHeightMode: Text.FixedHeight
@ -116,7 +116,7 @@ Rectangle
visible: !isNetworkConfigured && isNetworkConfigurable
text: catalog.i18nc("@info", "Please connect your printer to the network.")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap
width: contentWidth
lineHeight: UM.Theme.getSize("monitor_text_line_large").height

View file

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

View file

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

View file

@ -261,7 +261,11 @@ class PostProcessingPlugin(QObject, Extension):
script_str = script_str.replace(r"\\\n", "\n").replace(r"\\\\", "\\\\") # Unescape escape sequences.
script_parser = configparser.ConfigParser(interpolation=None)
script_parser.optionxform = str # type: ignore # Don't transform the setting keys as they are case-sensitive.
script_parser.read_string(script_str)
try:
script_parser.read_string(script_str)
except configparser.Error as e:
Logger.error("Stored post-processing scripts have syntax errors: {err}".format(err = str(e)))
continue
for script_name, settings in script_parser.items(): # There should only be one, really! Otherwise we can't guarantee the order or allow multiple uses of the same script.
if script_name == "DEFAULT": # ConfigParser always has a DEFAULT section, but we don't fill it. Ignore this one.
continue

View file

@ -7,6 +7,7 @@
# tries to create PyQt objects on a non-main thread.
import Arcus # @UnusedImport
import Savitar # @UnusedImport
import pynest2d # @UnusedImport
from . import PostProcessingPlugin

View file

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

View file

@ -889,7 +889,7 @@ class ChangeAtZProcessor:
# set feedrate percentage
if "speed" in values:
codes.append("M220 S" + str(values["speed"]) + " T1")
codes.append("M220 S" + str(values["speed"]) + "")
# set print rate percentage
if "printspeed" in values:
@ -1305,7 +1305,7 @@ class ChangeAtZProcessor:
self.targetLayer = None
self.targetZ = None
self.layerHeight = None
self.lastValues = {}
self.lastValues = {"speed": 100}
self.linearRetraction = True
self.insideTargetLayer = False
self.targetValuesInjected = False

View file

@ -1,7 +1,7 @@
# Cura PostProcessingPlugin
# Author: Mathias Lyngklip Kjeldgaard, Alexander Gee
# Author: Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen
# Date: July 31, 2019
# Modified: May 22, 2020
# Modified: Okt 22, 2020
# Description: This plugin displays progress on the LCD. It can output the estimated time remaining and the completion percentage.
@ -26,10 +26,31 @@ class DisplayProgressOnLCD(Script):
"time_remaining":
{
"label": "Time Remaining",
"description": "When enabled, write Time Left: HHMMSS on the display using M117. This is updated every layer.",
"description": "Select to write remaining time to the display.Select to write remaining time on the display using M117 status line message (almost all printers) or using M73 command (Prusa and Marlin 2 if enabled).",
"type": "bool",
"default_value": false
},
"time_remaining_method":
{
"label": "Time Reporting Method",
"description": "How should remaining time be shown on the display? It could use a generic message command (M117, almost all printers), or a specialised time remaining command (M73, Prusa and Marlin 2).",
"type": "enum",
"options": {
"m117":"M117 - All printers",
"m73":"M73 - Prusa, Marlin 2"
},
"enabled": "time_remaining",
"default_value": "m117"
},
"update_frequency":
{
"label": "Update frequency",
"description": "Update remaining time for every layer or periodically every minute or faster.",
"type": "enum",
"options": {"0":"Every layer","15":"Every 15 seconds","30":"Every 30 seconds","60":"Every minute"},
"default_value": "0",
"enabled": "time_remaining"
},
"percentage":
{
"label": "Percentage",
@ -46,34 +67,44 @@ class DisplayProgressOnLCD(Script):
list_split = re.split(":", line) # Split at ":" so we can get the numerical value
return float(list_split[1]) # Convert the numerical portion to a float
def outputTime(self, lines, line_index, time_left):
def outputTime(self, lines, line_index, time_left, mode):
# Do some math to get the time left in seconds into the right format. (HH,MM,SS)
time_left = max(time_left, 0)
m, s = divmod(time_left, 60)
h, m = divmod(m, 60)
# Create the string
current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s))
# And now insert that into the GCODE
lines.insert(line_index, "M117 Time Left {}".format(current_time_string))
if mode == "m117":
current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s))
# And now insert that into the GCODE
lines.insert(line_index, "M117 Time Left {}".format(current_time_string))
else: # Must be m73.
mins = int(60 * h + m + s / 30)
lines.insert(line_index, "M73 R{}".format(mins))
def execute(self, data):
output_time = self.getSettingValueByKey("time_remaining")
output_time_method = self.getSettingValueByKey("time_remaining_method")
output_frequency = int(self.getSettingValueByKey("update_frequency"))
output_percentage = self.getSettingValueByKey("percentage")
line_set = {}
if output_percentage or output_time:
total_time = -1
previous_layer_end_percentage = 0
previous_layer_end_time = 0
for layer in data:
layer_index = data.index(layer)
lines = layer.split("\n")
for line in lines:
if line.startswith(";TIME:") and total_time == -1:
if (line.startswith(";TIME:") or line.startswith(";PRINT.TIME:")) and total_time == -1:
# This line represents the total time required to print the gcode
total_time = self.getTimeValue(line)
line_index = lines.index(line)
# In the beginning we may have 2 M73 lines, but it makes logic less complicated
if output_time:
self.outputTime(lines, line_index, total_time)
self.outputTime(lines, line_index, total_time, output_time_method)
if output_percentage:
# Emit 0 percent to sure Marlin knows we are overriding the completion percentage
lines.insert(line_index, "M73 P0")
@ -96,8 +127,34 @@ class DisplayProgressOnLCD(Script):
line_index = lines.index(line)
if output_time:
# Here we calculate remaining time
self.outputTime(lines, line_index, total_time - current_time)
if output_frequency == 0:
# Here we calculate remaining time
self.outputTime(lines, line_index, total_time - current_time, output_time_method)
else:
# Here we calculate remaining time and how many outputs are expected for the layer
layer_time_delta = int(current_time - previous_layer_end_time)
layer_step_delta = int((current_time - previous_layer_end_time) / output_frequency)
# If this layer represents less than 1 step then we don't need to emit anything, continue to the next layer
if layer_step_delta != 0:
# Grab the index of the current line and figure out how many lines represent one second
step = line_index / layer_time_delta
# Move new lines further as we add new lines above it
lines_added = 1
# Run through layer in seconds
for seconds in range(1, layer_time_delta + 1):
# Time from start to decide when to update
line_time = int(previous_layer_end_time + seconds)
# Output every X seconds and after last layer
if line_time % output_frequency == 0 or line_time == total_time:
# Line to add the output
time_line_index = int((seconds * step) + lines_added)
# Insert remaining time into the GCODE
self.outputTime(lines, time_line_index, total_time - line_time, output_time_method)
# Next line will be again lower
lines_added = lines_added + 1
previous_layer_end_time = int(current_time)
if output_percentage:
# Calculate percentage value this layer ends at

View file

@ -13,7 +13,7 @@ from ..PostProcessingPlugin import PostProcessingPlugin
# not sure if needed
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.getPluginPath = MagicMock(return_value = "mocked_plugin_path")

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2020 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Math.Color import Color
@ -112,9 +112,9 @@ class SimulationPass(RenderPass):
elif isinstance(node, NozzleNode):
nozzle_node = node
nozzle_node.setVisible(False)
nozzle_node.setVisible(False) # Don't set to true, we render it separately!
elif getattr(node, "_outside_buildarea", False) and isinstance(node, SceneNode) and node.getMeshData() and node.isVisible():
elif getattr(node, "_outside_buildarea", False) and isinstance(node, SceneNode) and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonPrintingMesh"):
disabled_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData())
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
@ -189,7 +189,6 @@ class SimulationPass(RenderPass):
# but the user is not using the layer slider, and the compatibility mode is not enabled
if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity() and nozzle_node is not None:
if head_position is not None:
nozzle_node.setVisible(True)
nozzle_node.setPosition(head_position)
nozzle_batch = RenderBatch(self._nozzle_shader, type = RenderBatch.RenderType.Transparent)
nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh = nozzle_node.getMeshData())

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides the Simulation view.",
"api": "7.3.0",
"api": "7.4.0",
"i18n-catalog": "cura"
}

View file

@ -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.
import json
@ -116,6 +116,7 @@ class SliceInfo(QObject, Extension):
machine_manager = self._application.getMachineManager()
print_information = self._application.getPrintInformation()
user_profile = self._application.getCuraAPI().account.userProfile
global_stack = machine_manager.activeMachine
@ -124,6 +125,8 @@ class SliceInfo(QObject, Extension):
data["schema_version"] = 0
data["cura_version"] = self._application.getVersion()
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")
if active_mode == 0:

View file

@ -1,12 +1,17 @@
<html>
<body>
<b>Cura Version:</b> 4.0<br/>
<b>Cura Version:</b> 4.8<br/>
<b>Operating System:</b> Windows 10<br/>
<b>Language:</b> en_US<br/>
<b>Machine Type:</b> Ultimaker S5<br/>
<b>Intent Profile:</b> Default<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>
<ul>

View file

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

View file

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

View file

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

View file

@ -2,6 +2,6 @@
"name": "Toolbox",
"author": "Ultimaker B.V.",
"version": "1.0.1",
"api": "7.3.0",
"api": "7.4.0",
"description": "Find, manage and install new Cura packages."
}

View file

@ -42,8 +42,7 @@ UM.Dialog
Row {
id: packageRow
anchors.left: parent.left
anchors.right: parent.right
Layout.fillWidth: true
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
leftPadding: UM.Theme.getSize("narrow_margin").width

View file

@ -140,8 +140,7 @@ class CloudPackageChecker(QObject):
sync_message = Message(self._i18n_catalog.i18nc(
"@info:generic",
"Do you want to sync material and software packages with your account?"),
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ),
lifetime = 0)
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
sync_message.addAction("sync",
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
icon = "",

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,014 KiB

After

Width:  |  Height:  |  Size: 899 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 774 KiB

After

Width:  |  Height:  |  Size: 682 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 KiB

After

Width:  |  Height:  |  Size: 430 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before After
Before After

View file

@ -60,7 +60,7 @@ Item
Label
{
id: buildplateLabel
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("default") // 12pt, regular
text: buildplate ? buildplate : ""

View file

@ -97,7 +97,7 @@ Item
height: width // TODO: Theme!
sourceSize.width: width // TODO: Theme!
sourceSize.height: width // TODO: Theme!
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
source: UM.Theme.getIcon("arrow_left")
}
}
@ -176,7 +176,7 @@ Item
height: width // TODO: Theme!
sourceSize.width: width // TODO: Theme!
sourceSize.height: width // TODO: Theme!
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
source: UM.Theme.getIcon("arrow_right")
}
}

View file

@ -18,7 +18,7 @@ Button
width: base.width
}
contentItem: Label {
color: enabled ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled")
color: enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
font.pixelSize: 32 * screenScaleFactor
horizontalAlignment: Text.AlignHCenter
text: base.text

View file

@ -57,7 +57,7 @@ Item
{
id: materialLabel
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("default") // 12pt, regular
text: ""
@ -87,7 +87,7 @@ Item
{
id: printCoreLabel
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("default_bold") // 12pt, bold
text: ""

View file

@ -39,7 +39,7 @@ Item
{
id: positionLabel
font: UM.Theme.getFont("small")
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
height: Math.round(size / 2)
horizontalAlignment: Text.AlignHCenter
text: position + 1

View file

@ -58,7 +58,7 @@ Item
Label
{
text: printJob && printJob.name ? printJob.name : ""
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
visible: printJob
@ -89,7 +89,7 @@ Item
Label
{
text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : ""
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
visible: printJob
@ -120,7 +120,7 @@ Item
{
id: printerAssignmentLabel
anchors.verticalCenter: parent.verticalCenter
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
text: {
@ -188,7 +188,7 @@ Item
Label {
text: printJob && printJob.owner ? printJob.owner : ""
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.top: printerConfiguration.top

View file

@ -44,7 +44,7 @@ Item
verticalCenter: parent.verticalCenter
}
text: printJob ? Math.round(printJob.progress * 100) + "%" : "0%"
color: printJob && printJob.isActive ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled")
color: printJob && printJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
width: contentWidth
font: UM.Theme.getFont("default") // 12pt, regular
@ -62,7 +62,7 @@ Item
leftMargin: UM.Theme.getSize("monitor_margin").width
verticalCenter: parent.verticalCenter
}
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
text:
{

View file

@ -103,7 +103,7 @@ Item
Label
{
text: printer && printer.name ? printer.name : ""
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
width: parent.width
@ -130,7 +130,7 @@ Item
anchors
{
top: printerNameLabel.bottom
topMargin: 6 * screenScaleFactor // TODO: Theme!
topMargin: UM.Theme.getSize("narrow_margin").height
left: printerNameLabel.left
}
text: printer ? printer.type : ""
@ -140,7 +140,7 @@ Item
id: managePrinterLink
anchors {
top: printerFamilyPill.bottom
topMargin: 6 * screenScaleFactor
topMargin: UM.Theme.getSize("narrow_margin").height
}
height: 18 * screenScaleFactor // TODO: Theme!
width: childrenRect.width
@ -160,7 +160,7 @@ Item
anchors
{
left: managePrinterText.right
leftMargin: 6 * screenScaleFactor
leftMargin: UM.Theme.getSize("narrow_margin").width
verticalCenter: managePrinterText.verticalCenter
}
color: UM.Theme.getColor("text_link")
@ -194,8 +194,7 @@ Item
var configs = []
if (printer)
{
configs.push(printer.printerConfiguration.extruderConfigurations[0])
configs.push(printer.printerConfiguration.extruderConfigurations[1])
configs = configs.concat(printer.printerConfiguration.extruderConfigurations)
}
else
{
@ -272,7 +271,8 @@ Item
}
// For cloud printing, add this mouse area over the disabled cameraButton to indicate that it's not available
MouseArea
//Warning message is commented out because it's factually incorrect. Fix CURA-7637 to allow camera connections via cloud.
/* MouseArea
{
id: cameraDisabledButtonArea
anchors.fill: cameraButton
@ -282,7 +282,7 @@ Item
enabled: !cameraButton.enabled
}
/* //Warning message is commented out because it's factually incorrect. Fix CURA-7637 to allow camera connections via cloud.
MonitorInfoBlurb
{
id: cameraDisabledInfo
@ -341,24 +341,33 @@ Item
{
verticalCenter: parent.verticalCenter
}
color: printer ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled")
color: printer ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
font: UM.Theme.getFont("large_bold") // 16pt, bold
text: {
if (!printer) {
return catalog.i18nc("@label:status", "Loading...")
}
if (printer && printer.state == "disabled")
if (printer.state == "disabled")
{
return catalog.i18nc("@label:status", "Unavailable")
}
if (printer && printer.state == "unreachable")
if (printer.state == "unreachable")
{
return catalog.i18nc("@label:status", "Unreachable")
}
if (printer && !printer.activePrintJob && printer.state == "idle")
if (!printer.activePrintJob && printer.state == "idle")
{
return catalog.i18nc("@label:status", "Idle")
}
if (!printer.activePrintJob && printer.state == "pre_print")
{
return catalog.i18nc("@label:status", "Preparing...")
}
if (!printer.activePrintJob && printer.state == "printing")
{
// The print job isn't quite updated yet.
return catalog.i18nc("@label:status", "Printing")
}
return ""
}
visible: text !== ""
@ -395,7 +404,7 @@ Item
Label
{
id: printerJobNameLabel
color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled")
color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
text: printer && printer.activePrintJob ? printer.activePrintJob.name : catalog.i18nc("@label", "Untitled")
@ -413,10 +422,10 @@ Item
anchors
{
top: printerJobNameLabel.bottom
topMargin: 6 * screenScaleFactor // TODO: Theme!
topMargin: UM.Theme.getSize("narrow_margin").height
left: printerJobNameLabel.left
}
color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled")
color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
elide: Text.ElideRight
font: UM.Theme.getFont("default") // 12pt, regular
text: printer && printer.activePrintJob ? printer.activePrintJob.owner : catalog.i18nc("@label", "Anonymous")
@ -448,7 +457,7 @@ Item
font: UM.Theme.getFont("default")
text: catalog.i18nc("@label:status", "Requires configuration changes")
visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
@ -484,7 +493,7 @@ Item
anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
color: UM.Theme.getColor("monitor_secondary_button_text")
font: UM.Theme.getFont("medium") // 14pt, regular
text: catalog.i18nc("@action:button","Details");
text: catalog.i18nc("@action:button", "Details");
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
height: 18 * screenScaleFactor // TODO: Theme!

View file

@ -27,7 +27,7 @@ Item
Label {
id: printerNameLabel
anchors.centerIn: parent
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
text: monitorPrinterPill.text
font.pointSize: 10 // TODO: Theme!
visible: monitorPrinterPill.text !== ""

View file

@ -26,7 +26,7 @@ Item
left: queuedPrintJobs.left
top: parent.top
}
color: UM.Theme.getColor("monitor_text_primary")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("large")
text: catalog.i18nc("@label", "Queued")
renderType: Text.NativeRendering
@ -58,7 +58,7 @@ Item
anchors
{
left: externalLinkIcon.right
leftMargin: 6 * screenScaleFactor // TODO: Theme!
leftMargin: UM.Theme.getSize("narrow_margin").width
verticalCenter: externalLinkIcon.verticalCenter
}
color: UM.Theme.getColor("text_link")
@ -88,7 +88,7 @@ Item
anchors
{
left: queuedPrintJobs.left
leftMargin: 6 * screenScaleFactor // TODO: Theme!
leftMargin: UM.Theme.getSize("narrow_margin").width
top: queuedLabel.bottom
topMargin: 24 * screenScaleFactor // TODO: Theme!
}
@ -97,14 +97,10 @@ Item
Label
{
text: catalog.i18nc("@label", "There are no print jobs in the queue. Slice and send a job to add one.")
color: UM.Theme.getColor("monitor_text_primary")
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium")
anchors.verticalCenter: parent.verticalCenter
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
visible: printJobList.count === 0
}
@ -112,15 +108,11 @@ Item
Label
{
text: catalog.i18nc("@label", "Print jobs")
color: UM.Theme.getColor("monitor_text_primary")
elide: Text.ElideRight
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 284 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
visible: printJobList.count > 0
}
@ -128,15 +120,11 @@ Item
Label
{
text: catalog.i18nc("@label", "Total print time")
color: UM.Theme.getColor("monitor_text_primary")
elide: Text.ElideRight
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: UM.Theme.getSize("monitor_column").width
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
visible: printJobList.count > 0
}
@ -144,15 +132,11 @@ Item
Label
{
text: catalog.i18nc("@label", "Waiting for")
color: UM.Theme.getColor("monitor_text_primary")
elide: Text.ElideRight
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: UM.Theme.getSize("monitor_column").width
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
visible: printJobList.count > 0
}

View file

@ -12,7 +12,7 @@ Button {
color: UM.Theme.getColor("monitor_context_menu_hover")
}
contentItem: Label {
color: enabled ? UM.Theme.getColor("monitor_text_primary") : UM.Theme.getColor("monitor_text_disabled");
color: enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled");
text: parent.text
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;

View file

@ -127,12 +127,16 @@ class CloudApiClient:
# \param cluster_id: The ID of the cluster.
# \param job_id: The ID of the print job.
# \param on_finished: The function to be called after the result is parsed.
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None:
# \param on_error: A function to be called if there was a server-side problem uploading. Generic errors (not
# specific to sending print jobs) such as lost connection, unparsable responses, etc. are not returned here, but
# handled in a generic way by the CloudApiClient.
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any], on_error) -> None:
url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id)
self._http.post(url,
scope = self._scope,
data = b"",
callback = self._parseCallback(on_finished, CloudPrintResponse),
error_callback = on_error,
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str,

View file

@ -3,10 +3,11 @@
from time import time
import os
from typing import List, Optional, cast
from typing import cast, List, Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest # Parse errors specific to print job uploading.
from UM import i18nCatalog
from UM.Backend.Backend import BackendState
@ -23,6 +24,7 @@ from ..ExportFileJob import ExportFileJob
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage
from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage
from ..Messages.PrintJobUploadQueueFullMessage import PrintJobUploadQueueFullMessage
from ..Messages.PrintJobUploadSuccessMessage import PrintJobUploadSuccessMessage
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
from ..Models.Http.CloudClusterStatus import CloudClusterStatus
@ -194,7 +196,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
# The mesh didn't change, let's not upload it to the cloud again.
# Note that self.writeFinished is called in _onPrintUploadCompleted as well.
if self._uploaded_print_job:
self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintUploadCompleted)
self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintUploadCompleted, self._onPrintUploadSpecificError)
return
# Export the scene to the correct file type.
@ -240,7 +242,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
if not print_job: # It's possible that another print job is requested in the meanwhile, which then fails to upload with an error, which sets self._uploaded_print_job to `None`.
# TODO: Maybe _onUploadError shouldn't set the _uploaded_print_job to None or we need to prevent such asynchronous cases.
return # Prevent a crash.
self._api.requestPrint(self.key, print_job.job_id, self._onPrintUploadCompleted)
self._api.requestPrint(self.key, print_job.job_id, self._onPrintUploadCompleted, self._onPrintUploadSpecificError)
def _onPrintUploadCompleted(self, response: CloudPrintResponse) -> None:
"""Shows a message when the upload has succeeded
@ -251,9 +253,23 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
PrintJobUploadSuccessMessage().show()
self.writeFinished.emit()
def _onUploadError(self, message: str = None) -> None:
"""Displays the given message if uploading the mesh has failed
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
"""
Displays a message when an error occurs specific to uploading print job (i.e. queue is full).
"""
error_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
if error_code == 409:
PrintJobUploadQueueFullMessage().show()
else:
PrintJobUploadErrorMessage(I18N_CATALOG.i18nc("@error:send", "Unknown error code when uploading print job: {0}", error_code)).show()
self._progress.hide()
self._uploaded_print_job = None
self.writeError.emit()
def _onUploadError(self, message: str = None) -> None:
"""
Displays the given message if uploading the mesh has failed due to a generic error (i.e. lost connection).
:param message: The message to display.
"""
self._progress.hide()

View file

@ -108,7 +108,11 @@ class ToolPathUploader:
Logger.log("i", "Finished callback %s %s",
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString())
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) # type: int
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) # type: Optional[int]
if not status_code:
Logger.log("e", "Reply contained no status code.")
self._errorCallback(reply, None)
return
# check if we should retry the last chunk
if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES:

View file

@ -0,0 +1,19 @@
# Copyright (c) 2020 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM import i18nCatalog
from UM.Message import Message
I18N_CATALOG = i18nCatalog("cura")
class PrintJobUploadQueueFullMessage(Message):
"""Message shown when uploading a print job to a cluster and the print queue is full."""
def __init__(self) -> None:
super().__init__(
text = I18N_CATALOG.i18nc("@info:status", "Print job queue is full. The printer can't accept a new job."),
title = I18N_CATALOG.i18nc("@info:title", "Queue Full"),
lifetime = 10
)

View file

@ -71,12 +71,7 @@ class ClusterPrinterStatus(BaseModel):
:param controller: - The controller of the model.
"""
# FIXME
# Note that we're using '2' here as extruder count. We have hardcoded this for now to prevent issues where the
# amount of extruders coming back from the API is actually lower (which it can be if a printer was just added
# to a cluster). This should be fixed in the future, probably also on the cluster API side.
model = PrinterOutputModel(controller, 2, firmware_version = self.firmware_version)
model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version)
self.updateOutputModel(model)
return model

View file

@ -81,6 +81,8 @@ class SendMaterialJob(Job):
container_registry = CuraApplication.getInstance().getContainerRegistry()
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).
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:
if root_material_id not in materials_to_send:

View file

@ -85,6 +85,8 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice):
self._timeout_time = 30
self._num_is_host_check_failed = 0
@pyqtProperty(str, constant=True)
def address(self) -> str:
"""The IP address of the printer."""
@ -293,8 +295,16 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice):
def _checkIfClusterHost(self):
"""Check is this device is a cluster host and takes the needed actions when it is not."""
if len(self._printers) < 1 and self.isConnected():
self._num_is_host_check_failed += 1
else:
self._num_is_host_check_failed = 0
# Since we request the status of the cluster itself way less frequent in the cloud, it can happen that a cloud
# printer reports having 0 printers (since they are offline!) but we haven't asked if the entire cluster is
# offline. (See CURA-7360)
# So by just counting a number of subsequent times that this has happened fixes the incorrect display.
if self._num_is_host_check_failed >= 6:
NotClusterHostMessage(self).show()
self.close()
CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(self.key)

View file

@ -62,7 +62,6 @@ class AutoDetectBaudJob(Job):
except ValueError:
continue
sleep(wait_bootloader) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
successful_responses = 0
serial.write(b"\n") # Ensure we clear out previous responses
serial.write(b"M105\n")
@ -73,13 +72,11 @@ class AutoDetectBaudJob(Job):
while timeout_time > time():
line = serial.readline()
if b"ok" in line and b"T:" in line:
successful_responses += 1
if successful_responses >= 1:
self.setResult(baud_rate)
Logger.log("d", "Detected baud rate {baud_rate} on serial {serial} on retry {retry} with after {time_elapsed:0.2f} seconds.".format(
serial = self._serial_port, baud_rate = baud_rate, retry = retry, time_elapsed = time() - start_timeout_time))
serial.close() # close serial port so it can be opened by the USBPrinterOutputDevice
return
self.setResult(baud_rate)
Logger.log("d", "Detected baud rate {baud_rate} on serial {serial} on retry {retry} with after {time_elapsed:0.2f} seconds.".format(
serial = self._serial_port, baud_rate = baud_rate, retry = retry, time_elapsed = time() - start_timeout_time))
serial.close() # close serial port so it can be opened by the USBPrinterOutputDevice
return
serial.write(b"M105\n")
sleep(15) # Give the printer some time to init and try again.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,9 @@
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 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.
import pytest