Merge branch 'master' into refactor_singleton_settingsbase

Resolved a conflict in CuraEngineBackend due to refactor removing JSON from the command while master added optional verbose mode in debug mode.

Contributes to issue CURA-4410.
This commit is contained in:
Ghostkeeper 2018-08-22 17:04:55 +02:00
commit 06525bed4b
No known key found for this signature in database
GPG key ID: 5252B696FB5E7C7A
110 changed files with 1801 additions and 1139 deletions

View file

@ -24,6 +24,7 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.Job import Job
from UM.Preferences import Preferences
from cura.Machines.VariantType import VariantType
from cura.Settings.CuraStackBuilder import CuraStackBuilder
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
@ -629,6 +630,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
type = "extruder_train")
extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks}
# Make sure that those extruders have the global stack as the next stack or later some value evaluation
# will fail.
for stack in extruder_stacks:
stack.setNextStack(global_stack, connect_signals = False)
Logger.log("d", "Workspace loading is checking definitions...")
# Get all the definition files & check if they exist. If not, add them.
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
@ -889,7 +895,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
parser = self._machine_info.variant_info.parser
variant_name = parser["general"]["name"]
from cura.Machines.VariantManager import VariantType
variant_type = VariantType.BUILD_PLATE
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
@ -905,7 +910,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
parser = extruder_info.variant_info.parser
variant_name = parser["general"]["name"]
from cura.Machines.VariantManager import VariantType
variant_type = VariantType.NOZZLE
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
@ -929,12 +933,16 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
root_material_id = extruder_info.root_material_id
root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
build_plate_id = global_stack.variant.getId()
# get material diameter of this extruder
machine_material_diameter = extruder_stack.materialDiameter
material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
extruder_stack.variant.getName(),
build_plate_id,
machine_material_diameter,
root_material_id)
if material_node is not None and material_node.getContainer() is not None:
extruder_stack.material = material_node.getContainer()

View file

@ -1,6 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import argparse #To run the engine in debug mode if the front-end is in debug mode.
from collections import defaultdict
import os
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
@ -178,7 +179,15 @@ class CuraEngineBackend(QObject, Backend):
# This is useful for debugging and used to actually start the engine.
# \return list of commands and args / parameters.
def getEngineCommand(self) -> List[str]:
return [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
command = [self._application.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.")
known_args = vars(parser.parse_known_args()[0])
if known_args["debug"]:
command.append("-vvv")
return command
## Emitted when we get a message containing print duration and material amount.
# This also implies the slicing has finished.

View file

@ -41,7 +41,7 @@ class StartJobResult(IntEnum):
## Formatter class that handles token expansion in start/end gcode
class GcodeStartEndFormatter(Formatter):
def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
def get_value(self, key: str, args: str, kwargs: dict, default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
# and a default_extruder_nr to use when no extruder_nr is specified

View file

@ -17,6 +17,10 @@ catalog = i18nCatalog("cura")
#
# If you're zipping g-code, you might as well use gzip!
class GCodeGzWriter(MeshWriter):
def __init__(self) -> None:
super().__init__(add_to_recent_files = False)
## Writes the gzipped g-code to a stream.
#
# Note that even though the function accepts a collection of nodes, the

View file

@ -16,7 +16,8 @@ def getMetaData():
"extension": file_extension,
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
"mime_type": "application/gzip",
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode,
"hide_in_file_dialog": True,
}]
}
}

View file

@ -47,7 +47,7 @@ class GCodeWriter(MeshWriter):
_setting_keyword = ";SETTING_"
def __init__(self):
super().__init__()
super().__init__(add_to_recent_files = False)
self._application = Application.getInstance()

View file

@ -28,7 +28,7 @@ class PauseAtHeight(Script):
"pause_height":
{
"label": "Pause Height",
"description": "At what height should the pause occur",
"description": "At what height should the pause occur?",
"unit": "mm",
"type": "float",
"default_value": 5.0,
@ -39,7 +39,7 @@ class PauseAtHeight(Script):
"pause_layer":
{
"label": "Pause Layer",
"description": "At what layer should the pause occur",
"description": "At what layer should the pause occur?",
"type": "int",
"value": "math.floor((pause_height - 0.27) / 0.1) + 1",
"minimum_value": "0",
@ -142,13 +142,14 @@ class PauseAtHeight(Script):
standby_temperature = self.getSettingValueByKey("standby_temperature")
firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value")
control_temperatures = Application.getInstance().getGlobalContainerStack().getProperty("machine_nozzle_temp_enabled", "value")
initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")
is_griffin = False
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
# use offset to calculate the current height: <current_height> = <current_z> - <layer_0_z>
layer_0_z = 0.
layer_0_z = 0
current_z = 0
got_first_g_cmd_on_layer_0 = False
current_t = 0 #Tracks the current extruder for tracking the target temperature.
@ -195,11 +196,10 @@ class PauseAtHeight(Script):
# This block is executed once, the first time there is a G
# command, to get the z offset (z for first positive layer)
if not got_first_g_cmd_on_layer_0:
layer_0_z = current_z
layer_0_z = current_z - initial_layer_height
got_first_g_cmd_on_layer_0 = True
current_height = current_z - layer_0_z
if current_height < pause_height:
break # Try the next layer.

View file

@ -40,33 +40,37 @@ Item {
property bool layersVisible: true
function getUpperValueFromSliderHandle () {
function getUpperValueFromSliderHandle() {
return upperHandle.getValue()
}
function setUpperValue (value) {
function setUpperValue(value) {
upperHandle.setValue(value)
updateRangeHandle()
}
function getLowerValueFromSliderHandle () {
function getLowerValueFromSliderHandle() {
return lowerHandle.getValue()
}
function setLowerValue (value) {
function setLowerValue(value) {
lowerHandle.setValue(value)
updateRangeHandle()
}
function updateRangeHandle () {
function updateRangeHandle() {
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
}
// set the active handle to show only one label at a time
function setActiveHandle (handle) {
function setActiveHandle(handle) {
activeHandle = handle
}
function normalizeValue(value) {
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
}
// slider track
Rectangle {
id: track
@ -188,6 +192,8 @@ Item {
// set the slider position based on the upper value
function setValue (value) {
// Normalize values between range, since using arrow keys will create out-of-the-range values
value = sliderRoot.normalizeValue(value)
UM.SimulationView.setCurrentLayer(value)
@ -274,6 +280,8 @@ Item {
// set the slider position based on the lower value
function setValue (value) {
// Normalize values between range, since using arrow keys will create out-of-the-range values
value = sliderRoot.normalizeValue(value)
UM.SimulationView.setMinimumLayer(value)

View file

@ -29,6 +29,7 @@ Item {
// value properties
property real maximumValue: 100
property real minimumValue: 0
property bool roundValues: true
property real handleValue: maximumValue
@ -47,6 +48,10 @@ Item {
rangeHandle.width = handle.x - sliderRoot.handleSize
}
function normalizeValue(value) {
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
}
// slider track
Rectangle {
id: track
@ -110,6 +115,8 @@ Item {
// set the slider position based on the value
function setValue (value) {
// Normalize values between range, since using arrow keys will create out-of-the-range values
value = sliderRoot.normalizeValue(value)
UM.SimulationView.setCurrentPath(value)

View file

@ -1,4 +1,4 @@
// Copyright (c) 2017 Ultimaker B.V.
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
@ -12,30 +12,43 @@ import Cura 1.0 as Cura
Item
{
id: base
width: {
if (UM.SimulationView.compatibilityMode) {
width:
{
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
} else {
}
else
{
return UM.Theme.getSize("layerview_menu_size").width;
}
}
height: {
if (viewSettings.collapsed) {
if (UM.SimulationView.compatibilityMode) {
if (viewSettings.collapsed)
{
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height;
}
return UM.Theme.getSize("layerview_menu_size_collapsed").height;
} else if (UM.SimulationView.compatibilityMode) {
}
else if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
} else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) {
}
else if (UM.Preferences.getValue("layerview/layer_view_type") == 0)
{
return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
} else {
}
else
{
return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
}
}
Behavior on height { NumberAnimation { duration: 100 } }
property var buttonTarget: {
property var buttonTarget:
{
if(parent != null)
{
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
@ -44,7 +57,8 @@ Item
return Qt.point(0,0)
}
Rectangle {
Rectangle
{
id: layerViewMenu
anchors.right: parent.right
anchors.top: parent.top
@ -83,7 +97,8 @@ Item
}
}
ColumnLayout {
ColumnLayout
{
id: viewSettings
property bool collapsed: false
@ -195,7 +210,8 @@ Item
width: width
}
Connections {
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
@ -212,18 +228,22 @@ Item
}
}
Repeater {
Repeater
{
model: Cura.ExtrudersModel{}
CheckBox {
CheckBox
{
id: extrudersModelCheckBox
checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == ""
onClicked: {
onClicked:
{
viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0
UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|"));
}
visible: !UM.SimulationView.compatibilityMode
enabled: index + 1 <= 4
Rectangle {
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: extrudersModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
@ -253,8 +273,10 @@ Item
}
}
Repeater {
model: ListModel {
Repeater
{
model: ListModel
{
id: typesLegendModel
Component.onCompleted:
{
@ -285,13 +307,16 @@ Item
}
}
CheckBox {
CheckBox
{
id: legendModelCheckBox
checked: model.initialValue
onClicked: {
onClicked:
{
UM.Preferences.setValue(model.preference, checked);
}
Rectangle {
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: legendModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
@ -320,18 +345,22 @@ Item
}
}
CheckBox {
CheckBox
{
checked: viewSettings.only_show_top_layers
onClicked: {
onClicked:
{
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
}
text: catalog.i18nc("@label", "Only Show Top Layers")
visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
CheckBox {
CheckBox
{
checked: viewSettings.top_layer_count == 5
onClicked: {
onClicked:
{
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
}
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
@ -339,8 +368,10 @@ Item
style: UM.Theme.styles.checkbox
}
Repeater {
model: ListModel {
Repeater
{
model: ListModel
{
id: typesLegendModelNoCheck
Component.onCompleted:
{
@ -355,11 +386,13 @@ Item
}
}
Label {
Label
{
text: label
visible: viewSettings.show_legend
id: typesLegendModelLabel
Rectangle {
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: typesLegendModelLabel.right
width: UM.Theme.getSize("layerview_legend_size").width
@ -378,30 +411,37 @@ Item
}
// Text for the minimum, maximum and units for the feedrates and layer thickness
Item {
Item
{
id: gradientLegend
visible: viewSettings.show_gradient
width: parent.width
height: UM.Theme.getSize("layerview_row").height
anchors {
anchors
{
topMargin: UM.Theme.getSize("slider_layerview_margin").height
horizontalCenter: parent.horizontalCenter
}
Label {
Label
{
text: minText()
anchors.left: parent.left
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function minText() {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
function minText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
}
}
@ -409,20 +449,25 @@ Item
}
}
Label {
Label
{
text: unitsText()
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function unitsText() {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
function unitsText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return "mm/s"
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return "mm"
}
}
@ -430,20 +475,25 @@ Item
}
}
Label {
Label
{
text: maxText()
anchors.right: parent.right
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function maxText() {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
function maxText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
}
}
@ -453,7 +503,8 @@ Item
}
// Gradient colors for feedrate
Rectangle { // In QML 5.9 can be changed by LinearGradient
Rectangle
{ // In QML 5.9 can be changed by LinearGradient
// Invert values because then the bar is rotated 90 degrees
id: feedrateGradient
visible: viewSettings.show_feedrate_gradient
@ -463,20 +514,25 @@ Item
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient {
GradientStop {
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(1, 0.5, 0, 1)
}
GradientStop {
GradientStop
{
position: 0.625
color: Qt.rgba(0.375, 0.5, 0, 1)
}
GradientStop {
GradientStop
{
position: 0.75
color: Qt.rgba(0.25, 1, 0, 1)
}
GradientStop {
GradientStop
{
position: 1.0
color: Qt.rgba(0, 0, 1, 1)
}
@ -484,7 +540,8 @@ Item
}
// Gradient colors for layer thickness (similar to parula colormap)
Rectangle { // In QML 5.9 can be changed by LinearGradient
Rectangle // In QML 5.9 can be changed by LinearGradient
{
// Invert values because then the bar is rotated 90 degrees
id: thicknessGradient
visible: viewSettings.show_thickness_gradient
@ -494,24 +551,30 @@ Item
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient {
GradientStop {
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(1, 1, 0, 1)
}
GradientStop {
GradientStop
{
position: 0.25
color: Qt.rgba(1, 0.75, 0.25, 1)
}
GradientStop {
GradientStop
{
position: 0.5
color: Qt.rgba(0, 0.75, 0.5, 1)
}
GradientStop {
GradientStop
{
position: 0.75
color: Qt.rgba(0, 0.375, 0.75, 1)
}
GradientStop {
GradientStop
{
position: 1.0
color: Qt.rgba(0, 0, 0.5, 1)
}
@ -520,19 +583,22 @@ Item
}
}
Item {
Item
{
id: slidersBox
width: parent.width
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
anchors {
anchors
{
top: parent.bottom
topMargin: UM.Theme.getSize("slider_layerview_margin").height
left: parent.left
}
PathSlider {
PathSlider
{
id: pathSlider
height: UM.Theme.getSize("slider_handle").width
@ -553,25 +619,29 @@ Item
rangeColor: UM.Theme.getColor("slider_groove_fill")
// update values when layer data changes
Connections {
Connections
{
target: UM.SimulationView
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
// make sure the slider handlers show the correct value after switching views
Component.onCompleted: {
Component.onCompleted:
{
pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
}
LayerSlider {
LayerSlider
{
id: layerSlider
width: UM.Theme.getSize("slider_handle").width
height: UM.Theme.getSize("layerview_menu_size").height
anchors {
anchors
{
top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top
topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0
right: parent.right
@ -593,7 +663,8 @@ Item
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
// update values when layer data changes
Connections {
Connections
{
target: UM.SimulationView
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
@ -601,45 +672,54 @@ Item
}
// make sure the slider handlers show the correct value after switching views
Component.onCompleted: {
Component.onCompleted:
{
layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
// Play simulation button
Button {
Button
{
id: playButton
iconSource: "./resources/simulation_resume.svg"
style: UM.Theme.styles.small_tool_button
visible: !UM.SimulationView.compatibilityMode
anchors {
anchors
{
verticalCenter: pathSlider.verticalCenter
}
property var status: 0 // indicates if it's stopped (0) or playing (1)
onClicked: {
switch(status) {
case 0: {
onClicked:
{
switch(status)
{
case 0:
{
resumeSimulation()
break
}
case 1: {
case 1:
{
pauseSimulation()
break
}
}
}
function pauseSimulation() {
function pauseSimulation()
{
UM.SimulationView.setSimulationRunning(false)
iconSource = "./resources/simulation_resume.svg"
simulationTimer.stop()
status = 0
}
function resumeSimulation() {
function resumeSimulation()
{
UM.SimulationView.setSimulationRunning(true)
iconSource = "./resources/simulation_pause.svg"
simulationTimer.start()
@ -652,7 +732,8 @@ Item
interval: 100
running: false
repeat: true
onTriggered: {
onTriggered:
{
var currentPath = UM.SimulationView.currentPath
var numPaths = UM.SimulationView.numPaths
var currentLayer = UM.SimulationView.currentLayer
@ -697,7 +778,8 @@ Item
}
}
FontMetrics {
FontMetrics
{
id: fontMetrics
font: UM.Theme.getFont("default")
}

View file

@ -25,10 +25,12 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from UM.Settings.SettingInstance import SettingInstance
import numpy
class SupportEraser(Tool):
def __init__(self):
super().__init__()
self._shortcut_key = Qt.Key_G
self._shortcut_key = Qt.Key_E
self._controller = self.getController()
self._selection_pass = None
@ -96,8 +98,7 @@ class SupportEraser(Tool):
node.setName("Eraser")
node.setSelectable(True)
mesh = MeshBuilder()
mesh.addCube(10,10,10)
mesh = self._createCube(10)
node.setMeshData(mesh.build())
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
@ -160,3 +161,28 @@ class SupportEraser(Tool):
self._skip_press = False
self._had_selection = has_selection
def _createCube(self, size):
mesh = MeshBuilder()
# Can't use MeshBuilder.addCube() because that does not get per-vertex normals
# Per-vertex normals require duplication of vertices
s = size / 2
verts = [ # 6 faces with 4 corners each
[-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s],
[-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s],
[ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s],
[-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s],
[-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s],
[ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s]
]
mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32))
indices = []
for i in range(0, 24, 4): # All 6 quads (12 triangles)
indices.append([i, i+2, i+1])
indices.append([i, i+3, i+2])
mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32))
mesh.calculateNormals()
return mesh

View file

@ -20,7 +20,7 @@ catalog = i18nCatalog("cura")
class UFPWriter(MeshWriter):
def __init__(self):
super().__init__()
super().__init__(add_to_recent_files = False)
self._snapshot = None
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot)

View file

@ -30,7 +30,12 @@ Component
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right:parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: Cura.MachineManager.printerOutputDevices[0].name
elide: Text.ElideRight
}
Rectangle

View file

@ -113,7 +113,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";")
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
if "application/x-ufp" not in machine_file_formats and self.printerType == "ultimaker3" and Version(self.firmwareVersion) >= Version("4.4"):
if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"):
machine_file_formats = ["application/x-ufp"] + machine_file_formats
# Take the intersection between file_formats and machine_file_formats.
@ -590,4 +590,4 @@ def findByKey(list: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: s
for item in list:
if item.key == key:
return item
return None
return None

View file

@ -299,11 +299,11 @@ Cura.MachineAction
}
else if (base.selectedDevice.clusterSize === 0)
{
return catalog.i18nc("@label", "This printer is not set up to host a group of Ultimaker 3 printers.");
return catalog.i18nc("@label", "This printer is not set up to host a group of printers.");
}
else
{
return catalog.i18nc("@label", "This printer is the host for a group of %1 Ultimaker 3 printers.".arg(base.selectedDevice.clusterSize));
return catalog.i18nc("@label", "This printer is the host for a group of %1 printers.".arg(base.selectedDevice.clusterSize));
}
}

View file

@ -16,9 +16,9 @@ Item
MouseArea
{
anchors.fill: parent
onClicked: OutputDevice.setActivePrinter(null)
z: 0
anchors.fill: parent
onClicked: OutputDevice.setActivePrinter(null)
z: 0
}
Button
@ -28,7 +28,7 @@ Item
anchors.bottomMargin: UM.Theme.getSize("default_margin").width
anchors.right: cameraImage.right
// TODO: Harcoded sizes
// TODO: Hardcoded sizes
width: 20 * screenScaleFactor
height: 20 * screenScaleFactor

View file

@ -39,12 +39,12 @@ class SendMaterialJob(Job):
try:
remote_materials_list = json.loads(remote_materials_list)
except json.JSONDecodeError:
Logger.log("e", "Current material storage on printer was a corrupted reply.")
Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
return
try:
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
except KeyError:
Logger.log("e", "Current material storage on printer was an invalid reply (missing GUIDs).")
Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.")
return
container_registry = ContainerRegistry.getInstance()

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Job import Job
@ -21,7 +21,6 @@ class AutoDetectBaudJob(Job):
def run(self):
Logger.log("d", "Auto detect baud rate started.")
timeout = 3
wait_response_timeouts = [3, 15, 30]
wait_bootloader_times = [1.5, 5, 15]
write_timeout = 3
@ -52,7 +51,7 @@ class AutoDetectBaudJob(Job):
if serial is None:
try:
serial = Serial(str(self._serial_port), baud_rate, timeout = read_timeout, writeTimeout = write_timeout)
except SerialException as e:
except SerialException:
Logger.logException("w", "Unable to create serial")
continue
else:

View file

@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
from .AutoDetectBaudJob import AutoDetectBaudJob
from .avr_isp import stk500v2, intelHex
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
from serial import Serial, SerialException, SerialTimeoutException
from threading import Thread, Event
@ -146,8 +146,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
@pyqtSlot(str)
def updateFirmware(self, file):
# the file path is qurl encoded.
self._firmware_location = file.replace("file://", "")
# the file path could be url-encoded.
if file.startswith("file://"):
self._firmware_location = QUrl(file).toLocalFile()
else:
self._firmware_location = file
self.showFirmwareInterface()
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
self._update_firmware_thread.start()

View file

@ -8,6 +8,9 @@ from UM.VersionUpgrade import VersionUpgrade
deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"}
changed_settings = {'retraction_combing': 'noskin'}
updated_settings = {'retraction_combing': 'infill'}
_RENAMED_MATERIAL_PROFILES = {
"dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder",
"dsm_arnitel2045_175_cartesio_0.4_mm": "dsm_arnitel2045_175_cartesio_0.4mm_thermoplastic_extruder",
@ -127,6 +130,13 @@ class VersionUpgrade34to40(VersionUpgrade):
continue
del parser["values"][deleted_setting]
for setting_key in changed_settings:
if setting_key not in parser["values"]:
continue
if parser["values"][setting_key] == changed_settings[setting_key]:
parser["values"][setting_key] = updated_settings[setting_key]
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View file

@ -6,21 +6,22 @@ import io
import json #To parse the product-to-id mapping file.
import os.path #To find the product-to-id mapping.
import sys
from typing import Any, Dict, List, Optional, cast
from typing import Any, Dict, List, Optional, Tuple, cast
import xml.etree.ElementTree as ET
from typing import Dict
from typing import Iterator
from UM.Resources import Resources
from UM.Logger import Logger
from cura.CuraApplication import CuraApplication
import UM.Dictionary
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from cura.CuraApplication import CuraApplication
from cura.Machines.VariantType import VariantType
from .XmlMaterialValidator import XmlMaterialValidator
## Handles serializing and deserializing material containers from an XML file
class XmlMaterialProfile(InstanceContainer):
CurrentFdmMaterialVersion = "1.3"
@ -269,7 +270,6 @@ class XmlMaterialProfile(InstanceContainer):
buildplate_dict = {} # type: Dict[str, Any]
for variant_name, variant_dict in machine_variant_map[definition_id].items():
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
from cura.Machines.VariantManager import VariantType
variant_type = VariantType(variant_type)
if variant_type == VariantType.NOZZLE:
# The hotend identifier is not the containers name, but its "name".
@ -693,74 +693,38 @@ class XmlMaterialProfile(InstanceContainer):
if buildplate_id is None:
continue
from cura.Machines.VariantManager import VariantType
variant_manager = CuraApplication.getInstance().getVariantManager()
variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
variant_type = VariantType.BUILD_PLATE)
if not variant_node:
continue
buildplate_compatibility = machine_compatibility
buildplate_recommended = machine_compatibility
settings = buildplate.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__unmapped_settings:
if key == "hardware compatible":
buildplate_compatibility = self._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = self._parseCompatibleValue(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
_, buildplate_unmapped_settings_dict = self._getSettingsDictForNode(buildplate)
buildplate_compatibility = buildplate_unmapped_settings_dict.get("hardware compatible",
machine_compatibility)
buildplate_recommended = buildplate_unmapped_settings_dict.get("hardware recommended",
machine_compatibility)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
hotends = machine.iterfind("./um:hotend", self.__namespaces)
for hotend in hotends:
# The "id" field for hotends in material profiles are actually
# The "id" field for hotends in material profiles is actually name
hotend_name = hotend.get("id")
if hotend_name is None:
continue
variant_manager = CuraApplication.getInstance().getVariantManager()
variant_node = variant_manager.getVariantNode(machine_id, hotend_name)
variant_node = variant_manager.getVariantNode(machine_id, hotend_name, VariantType.NOZZLE)
if not variant_node:
continue
hotend_compatibility = machine_compatibility
hotend_setting_values = {}
settings = hotend.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__material_settings_setting_map:
if key == "processing temperature graph": #This setting has no setting text but subtags.
graph_nodes = entry.iterfind("./um:point", self.__namespaces)
graph_points = []
for graph_node in graph_nodes:
flow = float(graph_node.get("flow"))
temperature = float(graph_node.get("temperature"))
graph_points.append([flow, temperature])
hotend_setting_values[self.__material_settings_setting_map[key]] = str(graph_points)
else:
hotend_setting_values[self.__material_settings_setting_map[key]] = entry.text
elif key in self.__unmapped_settings:
if key == "hardware compatible":
hotend_compatibility = self._parseCompatibleValue(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
# Add namespaced Cura-specific settings
settings = hotend.iterfind("./cura:setting", self.__namespaces)
for entry in settings:
value = entry.text
if value.lower() == "yes":
value = True
elif value.lower() == "no":
value = False
key = entry.get("key")
hotend_setting_values[key] = value
hotend_mapped_settings, hotend_unmapped_settings = self._getSettingsDictForNode(hotend)
hotend_compatibility = hotend_unmapped_settings.get("hardware compatible", machine_compatibility)
# Generate container ID for the hotend-specific material container
new_hotend_specific_material_id = self.getId() + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent material
@ -785,7 +749,7 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
cached_hotend_setting_properties.update(hotend_setting_values)
cached_hotend_setting_properties.update(hotend_mapped_settings)
new_hotend_material.setCachedValues(cached_hotend_setting_properties)
@ -794,6 +758,61 @@ class XmlMaterialProfile(InstanceContainer):
if is_new_material:
containers_to_add.append(new_hotend_material)
#
# Build plates in hotend
#
buildplates = hotend.iterfind("./um:buildplate", self.__namespaces)
for buildplate in buildplates:
# The "id" field for buildplate in material profiles is actually name
buildplate_name = buildplate.get("id")
if buildplate_name is None:
continue
variant_manager = CuraApplication.getInstance().getVariantManager()
variant_node = variant_manager.getVariantNode(machine_id, buildplate_name, VariantType.BUILD_PLATE)
if not variant_node:
continue
buildplate_mapped_settings, buildplate_unmapped_settings = self._getSettingsDictForNode(buildplate)
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
buildplate_map["buildplate_compatible"])
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
buildplate_map["buildplate_recommended"])
# Generate container ID for the hotend-and-buildplate-specific material container
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent material
if ContainerRegistry.getInstance().isLoaded(new_hotend_and_buildplate_specific_material_id):
new_hotend_and_buildplate_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_and_buildplate_specific_material_id)[0]
is_new_material = False
else:
new_hotend_and_buildplate_material = XmlMaterialProfile(new_hotend_and_buildplate_specific_material_id)
is_new_material = True
new_hotend_and_buildplate_material.setMetaData(copy.deepcopy(new_hotend_material.getMetaData()))
new_hotend_and_buildplate_material.getMetaData()["id"] = new_hotend_and_buildplate_specific_material_id
new_hotend_and_buildplate_material.getMetaData()["name"] = self.getName()
new_hotend_and_buildplate_material.getMetaData()["variant_name"] = hotend_name
new_hotend_and_buildplate_material.getMetaData()["buildplate_name"] = buildplate_name
new_hotend_and_buildplate_material.setDefinition(machine_id)
# Don't use setMetadata, as that overrides it for all materials with same base file
new_hotend_and_buildplate_material.getMetaData()["compatible"] = buildplate_compatibility
new_hotend_and_buildplate_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
new_hotend_and_buildplate_material.getMetaData()["definition"] = machine_id
new_hotend_and_buildplate_material.getMetaData()["buildplate_compatible"] = buildplate_compatibility
new_hotend_and_buildplate_material.getMetaData()["buildplate_recommended"] = buildplate_recommended
cached_hotend_and_buildplate_setting_properties = cached_hotend_setting_properties.copy()
cached_hotend_and_buildplate_setting_properties.update(buildplate_mapped_settings)
new_hotend_and_buildplate_material.setCachedValues(cached_hotend_and_buildplate_setting_properties)
new_hotend_and_buildplate_material._dirty = False
if is_new_material:
containers_to_add.append(new_hotend_and_buildplate_material)
# there is only one ID for a machine. Once we have reached here, it means we have already found
# a workable ID for that machine, so there is no need to continue
break
@ -801,6 +820,54 @@ class XmlMaterialProfile(InstanceContainer):
for container_to_add in containers_to_add:
ContainerRegistry.getInstance().addContainer(container_to_add)
@classmethod
def _getSettingsDictForNode(cls, node) -> Tuple[dict, dict]:
node_mapped_settings_dict = dict()
node_unmapped_settings_dict = dict()
# Fetch settings in the "um" namespace
um_settings = node.iterfind("./um:setting", cls.__namespaces)
for um_setting_entry in um_settings:
setting_key = um_setting_entry.get("key")
# Mapped settings
if setting_key in cls.__material_settings_setting_map:
if setting_key == "processing temperature graph": # This setting has no setting text but subtags.
graph_nodes = um_setting_entry.iterfind("./um:point", cls.__namespaces)
graph_points = []
for graph_node in graph_nodes:
flow = float(graph_node.get("flow"))
temperature = float(graph_node.get("temperature"))
graph_points.append([flow, temperature])
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = str(
graph_points)
else:
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = um_setting_entry.text
# Unmapped settings
elif setting_key in cls.__unmapped_settings:
if setting_key in ("hardware compatible", "hardware recommended"):
node_unmapped_settings_dict[setting_key] = cls._parseCompatibleValue(um_setting_entry.text)
# Unknown settings
else:
Logger.log("w", "Unsupported material setting %s", setting_key)
# Fetch settings in the "cura" namespace
cura_settings = node.iterfind("./cura:setting", cls.__namespaces)
for cura_setting_entry in cura_settings:
value = cura_setting_entry.text
if value.lower() == "yes":
value = True
elif value.lower() == "no":
value = False
key = cura_setting_entry.get("key")
# Cura settings are all mapped
node_mapped_settings_dict[key] = value
return node_mapped_settings_dict, node_unmapped_settings_dict
@classmethod
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
result_metadata = [] #All the metadata that we found except the base (because the base is returned).
@ -983,6 +1050,36 @@ class XmlMaterialProfile(InstanceContainer):
result_metadata.append(new_hotend_material_metadata)
#
# Buildplates in Hotends
#
buildplates = hotend.iterfind("./um:buildplate", cls.__namespaces)
for buildplate in buildplates:
# The "id" field for buildplate in material profiles is actually name
buildplate_name = buildplate.get("id")
if buildplate_name is None:
continue
buildplate_mapped_settings, buildplate_unmapped_settings = cls._getSettingsDictForNode(buildplate)
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
buildplate_map["buildplate_compatible"])
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
buildplate_map["buildplate_recommended"])
# Generate container ID for the hotend-and-buildplate-specific material container
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(
" ", "_")
new_hotend_and_buildplate_material_metadata = {}
new_hotend_and_buildplate_material_metadata.update(new_hotend_material_metadata)
new_hotend_and_buildplate_material_metadata["id"] = new_hotend_and_buildplate_specific_material_id
new_hotend_and_buildplate_material_metadata["buildplate_name"] = buildplate_name
new_hotend_and_buildplate_material_metadata["compatible"] = buildplate_compatibility
new_hotend_and_buildplate_material_metadata["buildplate_compatible"] = buildplate_compatibility
new_hotend_and_buildplate_material_metadata["buildplate_recommended"] = buildplate_recommended
result_metadata.append(new_hotend_and_buildplate_material_metadata)
# there is only one ID for a machine. Once we have reached here, it means we have already found
# a workable ID for that machine, so there is no need to continue
break