mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-11-28 13:21:10 -07:00
Merge branch 'master' of github.com:Ultimaker/Cura into CURA-7162/replace_robot_with_placeholder
This commit is contained in:
commit
24e2fc89e5
52 changed files with 4859 additions and 2121 deletions
|
|
@ -422,13 +422,14 @@ class StartSliceJob(Job):
|
|||
|
||||
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
|
||||
start_gcode = settings["machine_start_gcode"]
|
||||
# Remove all the comments from the start g-code
|
||||
start_gcode = re.sub(r";.+?(\n|$)", "\n", start_gcode)
|
||||
bed_temperature_settings = ["material_bed_temperature", "material_bed_temperature_layer_0"]
|
||||
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(bed_temperature_settings) # match {setting} as well as {setting, extruder_nr}
|
||||
settings["material_bed_temp_prepend"] = re.search(pattern, start_gcode) == None
|
||||
print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"]
|
||||
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr}
|
||||
settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None
|
||||
|
||||
# Replace the setting tokens in start and end g-code.
|
||||
# Use values from the first used extruder by default so we get the expected temperatures
|
||||
initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
|
||||
|
|
|
|||
|
|
@ -169,6 +169,9 @@ class FlavorParser:
|
|||
# A threshold is set to avoid weird paths in the GCode
|
||||
if line_width > 1.2:
|
||||
return 0.35
|
||||
# Prevent showing infinitely wide lines
|
||||
if line_width < 0.0:
|
||||
return 0.0
|
||||
return line_width
|
||||
|
||||
def _gCode0(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
|
||||
|
|
@ -235,7 +238,7 @@ class FlavorParser:
|
|||
def _gCode92(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
|
||||
if params.e is not None:
|
||||
# Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width
|
||||
self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e
|
||||
self._extrusion_length_offset[self._extruder_number] = position.e[self._extruder_number] - params.e
|
||||
position.e[self._extruder_number] = params.e
|
||||
self._previous_extrusion_value = params.e
|
||||
else:
|
||||
|
|
@ -261,13 +264,13 @@ class FlavorParser:
|
|||
try:
|
||||
if item[0] == "X":
|
||||
x = float(item[1:])
|
||||
if item[0] == "Y":
|
||||
elif item[0] == "Y":
|
||||
y = float(item[1:])
|
||||
if item[0] == "Z":
|
||||
elif item[0] == "Z":
|
||||
z = float(item[1:])
|
||||
if item[0] == "F":
|
||||
elif item[0] == "F":
|
||||
f = float(item[1:]) / 60
|
||||
if item[0] == "E":
|
||||
elif item[0] == "E":
|
||||
e = float(item[1:])
|
||||
except ValueError: # Improperly formatted g-code: Coordinates are not floats.
|
||||
continue # Skip the command then.
|
||||
|
|
|
|||
|
|
@ -6,14 +6,22 @@
|
|||
#Authors of the 2-1 ColorMix plug-in / script:
|
||||
# Written by John Hryb - john.hryb.4@gmail.com
|
||||
|
||||
##history / change-log:
|
||||
##V1.0.0
|
||||
|
||||
## Uses -
|
||||
## M163 - Set Mix Factor
|
||||
## M164 - Save Mix - saves to T3 as a unique mix
|
||||
|
||||
import re #To perform the search and replace.
|
||||
#history / change-log:
|
||||
#V1.0.0 - Initial
|
||||
#V1.1.0 -
|
||||
# additions:
|
||||
#Object number - To select individual models or all when using "one at a time" print sequence
|
||||
#V1.2.0
|
||||
# fixed layer heights Cura starts at 1 while G-code starts at 0
|
||||
# removed notes
|
||||
# changed Units of measurement to Units
|
||||
#V1.2.1
|
||||
# Fixed mm bug when not in multiples of layer height
|
||||
# Uses -
|
||||
# M163 - Set Mix Factor
|
||||
# M164 - Save Mix - saves to T2 as a unique mix
|
||||
|
||||
import re #To perform the search and replace.
|
||||
from ..Script import Script
|
||||
|
||||
class ColorMix(Script):
|
||||
|
|
@ -22,20 +30,28 @@ class ColorMix(Script):
|
|||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name":"ColorMix 2-1",
|
||||
"name":"ColorMix 2-1 V1.2.1",
|
||||
"key":"ColorMix 2-1",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"measurement_units":
|
||||
"units_of_measurement":
|
||||
{
|
||||
"label": "Units of measurement",
|
||||
"label": "Units",
|
||||
"description": "Input value as mm or layer number.",
|
||||
"type": "enum",
|
||||
"options": {"mm":"mm","layer":"Layer"},
|
||||
"default_value": "layer"
|
||||
},
|
||||
"object_number":
|
||||
{
|
||||
"label": "Object Number",
|
||||
"description": "Select model to apply to for print one at a time print sequence. 0 = everything",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"start_height":
|
||||
{
|
||||
"label": "Start Height",
|
||||
|
|
@ -59,10 +75,10 @@ class ColorMix(Script):
|
|||
"type": "float",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0.1",
|
||||
"enabled": "c_behavior == 'blend_value'"
|
||||
"minimum_value_warning": "start_height",
|
||||
"enabled": "behavior == 'blend_value'"
|
||||
},
|
||||
"mix_start_ratio":
|
||||
"mix_start":
|
||||
{
|
||||
"label": "Start mix ratio",
|
||||
"description": "First extruder percentage 0-100",
|
||||
|
|
@ -72,7 +88,7 @@ class ColorMix(Script):
|
|||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "100"
|
||||
},
|
||||
"mix_finish_ratio":
|
||||
"mix_finish":
|
||||
{
|
||||
"label": "End mix ratio",
|
||||
"description": "First extruder percentage 0-100 to finish blend",
|
||||
|
|
@ -81,14 +97,7 @@ class ColorMix(Script):
|
|||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled": "c_behavior == 'blend_value'"
|
||||
},
|
||||
"notes":
|
||||
{
|
||||
"label": "Notes",
|
||||
"description": "A spot to put a note",
|
||||
"type": "str",
|
||||
"default_value": ""
|
||||
"enabled": "behavior == 'blend_value'"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
|
@ -112,52 +121,53 @@ class ColorMix(Script):
|
|||
return default
|
||||
|
||||
def execute(self, data):
|
||||
#get user variables
|
||||
firstHeight = 0.0
|
||||
secondHeight = 0.0
|
||||
firstMix = 0.0
|
||||
SecondMix = 0.0
|
||||
|
||||
firstHeight = self.getSettingValueByKey("start_height")
|
||||
secondHeight = self.getSettingValueByKey("finish_height")
|
||||
firstMix = self.getSettingValueByKey("mix_start_ratio")
|
||||
SecondMix = self.getSettingValueByKey("mix_finish_ratio")
|
||||
|
||||
#locals
|
||||
layer = 0
|
||||
|
||||
firstMix = self.getSettingValueByKey("mix_start")
|
||||
secondMix = self.getSettingValueByKey("mix_finish")
|
||||
modelOfInterest = self.getSettingValueByKey("object_number")
|
||||
|
||||
#get layer height
|
||||
layerHeight = .2
|
||||
layerHeight = 0
|
||||
for active_layer in data:
|
||||
lines = active_layer.split("\n")
|
||||
for line in lines:
|
||||
if ";Layer height: " in line:
|
||||
layerHeight = self.getValue(line, ";Layer height: ", layerHeight)
|
||||
break
|
||||
if layerHeight != 0:
|
||||
break
|
||||
|
||||
#default layerHeight if not found
|
||||
if layerHeight == 0:
|
||||
layerHeight = .2
|
||||
|
||||
#get layers to use
|
||||
startLayer = 0
|
||||
endLayer = 0
|
||||
if self.getSettingValueByKey("measurement_units") == "mm":
|
||||
if firstHeight == 0:
|
||||
startLayer = 0
|
||||
else:
|
||||
startLayer = firstHeight / layerHeight
|
||||
if secondHeight == 0:
|
||||
endLayer = 0
|
||||
else:
|
||||
endLayer = secondHeight / layerHeight
|
||||
else: #layer height
|
||||
startLayer = firstHeight
|
||||
endLayer = secondHeight
|
||||
if self.getSettingValueByKey("units_of_measurement") == "mm":
|
||||
startLayer = round(firstHeight / layerHeight)
|
||||
endLayer = round(secondHeight / layerHeight)
|
||||
else: #layer height shifts down by one for g-code
|
||||
if firstHeight <= 0:
|
||||
firstHeight = 1
|
||||
if secondHeight <= 0:
|
||||
secondHeight = 1
|
||||
startLayer = firstHeight - 1
|
||||
endLayer = secondHeight - 1
|
||||
#see if one-shot
|
||||
if self.getSettingValueByKey("behavior") == "fixed_value":
|
||||
endLayer = startLayer
|
||||
firstExtruderIncrements = 0
|
||||
else: #blend
|
||||
firstExtruderIncrements = (SecondMix - firstMix) / (endLayer - startLayer)
|
||||
firstExtruderIncrements = (secondMix - firstMix) / (endLayer - startLayer)
|
||||
firstExtruderValue = 0
|
||||
index = 0
|
||||
|
||||
#start scanning
|
||||
layer = -1
|
||||
modelNumber = 0
|
||||
for active_layer in data:
|
||||
modified_gcode = ""
|
||||
lineIndex = 0;
|
||||
|
|
@ -169,22 +179,30 @@ class ColorMix(Script):
|
|||
# find current layer
|
||||
if ";LAYER:" in line:
|
||||
layer = self.getValue(line, ";LAYER:", layer)
|
||||
if (layer >= startLayer) and (layer <= endLayer): #find layers of interest
|
||||
if lines[lineIndex + 4] == "T2": #check if needing to delete old data
|
||||
del lines[(lineIndex + 1):(lineIndex + 5)]
|
||||
firstExtruderValue = int(((layer - startLayer) * firstExtruderIncrements) + firstMix)
|
||||
if firstExtruderValue == 100:
|
||||
modified_gcode += "M163 S0 P1\n"
|
||||
modified_gcode += "M163 S1 P0\n"
|
||||
elif firstExtruderValue == 0:
|
||||
modified_gcode += "M163 S0 P0\n"
|
||||
modified_gcode += "M163 S1 P1\n"
|
||||
else:
|
||||
modified_gcode += "M163 S0 P0.{:02d}\n".format(firstExtruderValue)
|
||||
modified_gcode += "M163 S1 P0.{:02d}\n".format(100 - firstExtruderValue)
|
||||
modified_gcode += "M164 S2\n"
|
||||
modified_gcode += "T2\n"
|
||||
#get model number by layer 0 repeats
|
||||
if layer == 0:
|
||||
modelNumber = modelNumber + 1
|
||||
#search for layers to manipulate
|
||||
if (layer >= startLayer) and (layer <= endLayer):
|
||||
#make sure correct model is selected
|
||||
if (modelOfInterest == 0) or (modelOfInterest == modelNumber):
|
||||
#Delete old data if required
|
||||
if lines[lineIndex + 4] == "T2":
|
||||
del lines[(lineIndex + 1):(lineIndex + 5)]
|
||||
#add mixing commands
|
||||
firstExtruderValue = int(((layer - startLayer) * firstExtruderIncrements) + firstMix)
|
||||
if firstExtruderValue == 100:
|
||||
modified_gcode += "M163 S0 P1\n"
|
||||
modified_gcode += "M163 S1 P0\n"
|
||||
elif firstExtruderValue == 0:
|
||||
modified_gcode += "M163 S0 P0\n"
|
||||
modified_gcode += "M163 S1 P1\n"
|
||||
else:
|
||||
modified_gcode += "M163 S0 P0.{:02d}\n".format(firstExtruderValue)
|
||||
modified_gcode += "M163 S1 P0.{:02d}\n".format(100 - firstExtruderValue)
|
||||
modified_gcode += "M164 S2\n"
|
||||
modified_gcode += "T2\n"
|
||||
lineIndex += 1 #for deleting index
|
||||
data[index] = modified_gcode
|
||||
index += 1
|
||||
return data
|
||||
return data
|
||||
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
from UM.Logger import LogOutput
|
||||
from typing import Set
|
||||
|
||||
from cura.CrashHandler import CrashHandler
|
||||
|
||||
try:
|
||||
from sentry_sdk import add_breadcrumb
|
||||
except ImportError:
|
||||
|
|
@ -10,8 +13,6 @@ except ImportError:
|
|||
from typing import Optional
|
||||
import os
|
||||
|
||||
home_dir = os.path.expanduser("~")
|
||||
|
||||
|
||||
class SentryLogger(LogOutput):
|
||||
# Sentry (https://sentry.io) is the service that Cura uses for logging crashes. This logger ensures that the
|
||||
|
|
@ -37,7 +38,7 @@ class SentryLogger(LogOutput):
|
|||
# \param message String containing message to be logged
|
||||
def log(self, log_type: str, message: str) -> None:
|
||||
level = self._translateLogType(log_type)
|
||||
message = self._pruneSensitiveData(message)
|
||||
message = CrashHandler.pruneSensitiveData(message)
|
||||
if level is None:
|
||||
if message not in self._show_once:
|
||||
level = self._translateLogType(log_type[0])
|
||||
|
|
@ -47,12 +48,6 @@ class SentryLogger(LogOutput):
|
|||
else:
|
||||
add_breadcrumb(level = level, message = message)
|
||||
|
||||
@staticmethod
|
||||
def _pruneSensitiveData(message):
|
||||
if home_dir in message:
|
||||
message = message.replace(home_dir, "<user_home>")
|
||||
return message
|
||||
|
||||
@staticmethod
|
||||
def _translateLogType(log_type: str) -> Optional[str]:
|
||||
return SentryLogger._levels.get(log_type)
|
||||
|
|
|
|||
|
|
@ -46,10 +46,19 @@ class SimulationPass(RenderPass):
|
|||
self._layer_view = layerview
|
||||
self._compatibility_mode = layerview.getCompatibilityMode()
|
||||
|
||||
def _updateLayerShaderValues(self):
|
||||
def render(self):
|
||||
if not self._layer_shader:
|
||||
if self._compatibility_mode:
|
||||
shader_filename = "layers.shader"
|
||||
shadow_shader_filename = "layers_shadow.shader"
|
||||
else:
|
||||
shader_filename = "layers3d.shader"
|
||||
shadow_shader_filename = "layers3d_shadow.shader"
|
||||
self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shader_filename))
|
||||
self._layer_shadow_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shadow_shader_filename))
|
||||
self._current_shader = self._layer_shader
|
||||
# Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
|
||||
self._layer_shader.setUniformValue("u_active_extruder",
|
||||
float(max(0, self._extruder_manager.activeExtruderIndex)))
|
||||
self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
|
||||
if self._layer_view:
|
||||
self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
|
||||
self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
|
||||
|
|
@ -62,7 +71,7 @@ class SimulationPass(RenderPass):
|
|||
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
|
||||
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
|
||||
else:
|
||||
# defaults
|
||||
#defaults
|
||||
self._layer_shader.setUniformValue("u_max_feedrate", 1)
|
||||
self._layer_shader.setUniformValue("u_min_feedrate", 0)
|
||||
self._layer_shader.setUniformValue("u_max_thickness", 1)
|
||||
|
|
@ -74,20 +83,6 @@ class SimulationPass(RenderPass):
|
|||
self._layer_shader.setUniformValue("u_show_skin", 1)
|
||||
self._layer_shader.setUniformValue("u_show_infill", 1)
|
||||
|
||||
def render(self):
|
||||
if not self._layer_shader:
|
||||
if self._compatibility_mode:
|
||||
shader_filename = "layers.shader"
|
||||
shadow_shader_filename = "layers_shadow.shader"
|
||||
else:
|
||||
shader_filename = "layers3d.shader"
|
||||
shadow_shader_filename = "layers3d_shadow.shader"
|
||||
self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shader_filename))
|
||||
self._layer_shadow_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shadow_shader_filename))
|
||||
self._current_shader = self._layer_shader
|
||||
|
||||
self._updateLayerShaderValues()
|
||||
|
||||
if not self._tool_handle_shader:
|
||||
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
|
||||
|
||||
|
|
@ -102,6 +97,7 @@ class SimulationPass(RenderPass):
|
|||
nozzle_node = None
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
|
||||
if isinstance(node, ToolHandle):
|
||||
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
||||
|
||||
|
|
@ -116,24 +112,29 @@ class SimulationPass(RenderPass):
|
|||
|
||||
# Render all layers below a certain number as line mesh instead of vertices.
|
||||
if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
|
||||
start = self._layer_view.start_elements_index
|
||||
end = self._layer_view.end_elements_index
|
||||
index = self._layer_view._current_path_num
|
||||
offset = 0
|
||||
layer = layer_data.getLayer(self._layer_view._current_layer_num)
|
||||
if layer is None:
|
||||
continue
|
||||
for polygon in layer.polygons:
|
||||
# The size indicates all values in the two-dimension array, and the second dimension is
|
||||
# always size 3 because we have 3D points.
|
||||
if index >= polygon.data.size // 3 - offset:
|
||||
index -= polygon.data.size // 3 - offset
|
||||
offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
|
||||
continue
|
||||
# The head position is calculated and translated
|
||||
head_position = Vector(polygon.data[index + offset][0], polygon.data[index + offset][1],
|
||||
polygon.data[index + offset][2]) + node.getWorldPosition()
|
||||
break
|
||||
start = 0
|
||||
end = 0
|
||||
element_counts = layer_data.getElementCounts()
|
||||
for layer in sorted(element_counts.keys()):
|
||||
# In the current layer, we show just the indicated paths
|
||||
if layer == self._layer_view._current_layer_num:
|
||||
# We look for the position of the head, searching the point of the current path
|
||||
index = self._layer_view._current_path_num
|
||||
offset = 0
|
||||
for polygon in layer_data.getLayer(layer).polygons:
|
||||
# The size indicates all values in the two-dimension array, and the second dimension is
|
||||
# always size 3 because we have 3D points.
|
||||
if index >= polygon.data.size // 3 - offset:
|
||||
index -= polygon.data.size // 3 - offset
|
||||
offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
|
||||
continue
|
||||
# The head position is calculated and translated
|
||||
head_position = Vector(polygon.data[index+offset][0], polygon.data[index+offset][1], polygon.data[index+offset][2]) + node.getWorldPosition()
|
||||
break
|
||||
break
|
||||
if self._layer_view._minimum_layer_num > layer:
|
||||
start += element_counts[layer]
|
||||
end += element_counts[layer]
|
||||
|
||||
# Calculate the range of paths in the last layer
|
||||
current_layer_start = end
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ class SimulationView(CuraView):
|
|||
LAYER_VIEW_TYPE_FEEDRATE = 2
|
||||
LAYER_VIEW_TYPE_THICKNESS = 3
|
||||
|
||||
_no_layers_warning_preference = "view/no_layers_warning"
|
||||
|
||||
def __init__(self, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
|
|
@ -71,8 +73,6 @@ class SimulationView(CuraView):
|
|||
self._max_paths = 0
|
||||
self._current_path_num = 0
|
||||
self._minimum_path_num = 0
|
||||
self.start_elements_index = 0
|
||||
self.end_elements_index = 0
|
||||
self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
|
||||
|
||||
self._busy = False
|
||||
|
|
@ -118,7 +118,10 @@ class SimulationView(CuraView):
|
|||
|
||||
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled."),
|
||||
title = catalog.i18nc("@info:title", "Simulation View"))
|
||||
self._slice_first_warning_message = Message(catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."), title = catalog.i18nc("@info:title", "No layers to show"))
|
||||
self._slice_first_warning_message = Message(catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."), title = catalog.i18nc("@info:title", "No layers to show"),
|
||||
option_text = catalog.i18nc("@info:option_text", "Do not show this message again"), option_state = False)
|
||||
self._slice_first_warning_message.optionToggled.connect(self._onDontAskMeAgain)
|
||||
CuraApplication.getInstance().getPreferences().addPreference(self._no_layers_warning_preference, True)
|
||||
|
||||
QtApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
|
|
@ -247,7 +250,6 @@ class SimulationView(CuraView):
|
|||
self._minimum_layer_num = self._current_layer_num
|
||||
|
||||
self._startUpdateTopLayers()
|
||||
self.recalculateStartEndElements()
|
||||
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
|
|
@ -262,7 +264,7 @@ class SimulationView(CuraView):
|
|||
self._current_layer_num = self._minimum_layer_num
|
||||
|
||||
self._startUpdateTopLayers()
|
||||
self.recalculateStartEndElements()
|
||||
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
def setPath(self, value: int) -> None:
|
||||
|
|
@ -276,7 +278,6 @@ class SimulationView(CuraView):
|
|||
self._minimum_path_num = self._current_path_num
|
||||
|
||||
self._startUpdateTopLayers()
|
||||
self.recalculateStartEndElements()
|
||||
self.currentPathNumChanged.emit()
|
||||
|
||||
def setMinimumPath(self, value: int) -> None:
|
||||
|
|
@ -364,24 +365,6 @@ class SimulationView(CuraView):
|
|||
return 0.0 # If it's still max-float, there are no measurements. Use 0 then.
|
||||
return self._min_thickness
|
||||
|
||||
def recalculateStartEndElements(self):
|
||||
self.start_elements_index = 0
|
||||
self.end_elements_index = 0
|
||||
scene = self.getController().getScene()
|
||||
for node in DepthFirstIterator(scene.getRoot()): # type: ignore
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
||||
# Found a the layer data!
|
||||
element_counts = layer_data.getElementCounts()
|
||||
for layer in sorted(element_counts.keys()):
|
||||
if layer == self._current_layer_num:
|
||||
break
|
||||
if self._minimum_layer_num > layer:
|
||||
self.start_elements_index += element_counts[layer]
|
||||
self.end_elements_index += element_counts[layer]
|
||||
|
||||
def getMaxThickness(self) -> float:
|
||||
return self._max_thickness
|
||||
|
||||
|
|
@ -603,7 +586,6 @@ class SimulationView(CuraView):
|
|||
def _startUpdateTopLayers(self) -> None:
|
||||
if not self._compatibility_mode:
|
||||
return
|
||||
self.recalculateStartEndElements()
|
||||
if self._top_layers_job:
|
||||
self._top_layers_job.finished.disconnect(self._updateCurrentLayerMesh)
|
||||
self._top_layers_job.cancel()
|
||||
|
|
@ -666,11 +648,15 @@ class SimulationView(CuraView):
|
|||
self._updateWithPreferences()
|
||||
|
||||
def _updateSliceWarningVisibility(self):
|
||||
if not self.getActivity():
|
||||
if not self.getActivity()\
|
||||
and not CuraApplication.getInstance().getPreferences().getValue("general/auto_slice")\
|
||||
and CuraApplication.getInstance().getPreferences().getValue(self._no_layers_warning_preference):
|
||||
self._slice_first_warning_message.show()
|
||||
else:
|
||||
self._slice_first_warning_message.hide()
|
||||
|
||||
def _onDontAskMeAgain(self, checked: bool) -> None:
|
||||
CuraApplication.getInstance().getPreferences().setValue(self._no_layers_warning_preference, not checked)
|
||||
|
||||
class _CreateTopLayersJob(Job):
|
||||
def __init__(self, scene: "Scene", layer_number: int, solid_layers: int) -> None:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -23,6 +26,7 @@ class CloudPackageChecker(QObject):
|
|||
self._application = application # type: CuraApplication
|
||||
self._scope = UltimakerCloudScope(application)
|
||||
self._model = SubscribedPackagesModel()
|
||||
self._message = None # type: Optional[Message]
|
||||
|
||||
self._application.initializationFinished.connect(self._onAppInitialized)
|
||||
self._i18n_catalog = i18nCatalog("cura")
|
||||
|
|
@ -33,13 +37,16 @@ class CloudPackageChecker(QObject):
|
|||
def _onAppInitialized(self) -> None:
|
||||
self._package_manager = self._application.getPackageManager()
|
||||
# initial check
|
||||
self._fetchUserSubscribedPackages()
|
||||
self._onLoginStateChanged()
|
||||
# check again whenever the login state changes
|
||||
self._application.getCuraAPI().account.loginStateChanged.connect(self._fetchUserSubscribedPackages)
|
||||
self._application.getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
|
||||
def _fetchUserSubscribedPackages(self) -> None:
|
||||
def _onLoginStateChanged(self) -> None:
|
||||
if self._application.getCuraAPI().account.isLoggedIn:
|
||||
self._getUserSubscribedPackages()
|
||||
elif self._message is not None:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
|
||||
def _getUserSubscribedPackages(self) -> None:
|
||||
Logger.debug("Requesting subscribed packages metadata from server.")
|
||||
|
|
@ -90,16 +97,17 @@ class CloudPackageChecker(QObject):
|
|||
Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages")
|
||||
sync_message = Message(self._i18n_catalog.i18nc(
|
||||
"@info:generic",
|
||||
"\nDo 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)
|
||||
"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)
|
||||
sync_message.addAction("sync",
|
||||
name=self._i18n_catalog.i18nc("@action:button", "Sync"),
|
||||
icon="",
|
||||
description="Sync your Cloud subscribed packages to your local environment.",
|
||||
button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
|
||||
icon = "",
|
||||
description = "Sync your Cloud subscribed packages to your local environment.",
|
||||
button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
sync_message.actionTriggered.connect(self._onSyncButtonClicked)
|
||||
sync_message.show()
|
||||
self._message = sync_message
|
||||
|
||||
def _onSyncButtonClicked(self, sync_message: Message, sync_message_action: str) -> None:
|
||||
sync_message.hide()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import tempfile
|
||||
from typing import Dict, List, Any
|
||||
|
||||
|
|
@ -81,11 +84,9 @@ class DownloadPresenter:
|
|||
return DownloadPresenter(self._app)
|
||||
|
||||
def _createProgressMessage(self) -> Message:
|
||||
return Message(i18n_catalog.i18nc(
|
||||
"@info:generic",
|
||||
"\nSyncing..."),
|
||||
return Message(i18n_catalog.i18nc("@info:generic", "Syncing..."),
|
||||
lifetime = 0,
|
||||
use_inactivity_timer=False,
|
||||
use_inactivity_timer = False,
|
||||
progress = 0.0,
|
||||
title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ class DownloadPresenter:
|
|||
self._progress[package_id]["received"] = self._progress[package_id]["total"]
|
||||
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(mode ="wb+", suffix =".curapackage", delete = False) as temp_file:
|
||||
with tempfile.NamedTemporaryFile(mode = "wb+", suffix = ".curapackage", delete = False) as temp_file:
|
||||
bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
|
||||
while bytes_read:
|
||||
temp_file.write(bytes_read)
|
||||
|
|
|
|||
|
|
@ -67,17 +67,22 @@ class PackagesModel(ListModel):
|
|||
|
||||
links_dict = {}
|
||||
if "data" in package:
|
||||
# Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier
|
||||
# to process.
|
||||
link_list = package["data"]["links"] if "links" in package["data"] else []
|
||||
links_dict = {d["title"]: d["url"] for d in link_list}
|
||||
|
||||
# This code never gets executed because the API response does not contain "supported_configs" in it
|
||||
# It is so because 2y ago when this was created - it did contain it. But it was a prototype only
|
||||
# and never got to production. As agreed with the team, it'll stay here for now, in case we decide to rework and use it
|
||||
# The response payload has been changed. Please see:
|
||||
# https://github.com/Ultimaker/Cura/compare/CURA-7072-temp?expand=1
|
||||
if "supported_configs" in package["data"]:
|
||||
if len(package["data"]["supported_configs"]) > 0:
|
||||
has_configs = True
|
||||
configs_model = ConfigsModel()
|
||||
configs_model.setConfigs(package["data"]["supported_configs"])
|
||||
|
||||
# Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier
|
||||
# to process.
|
||||
link_list = package["data"]["links"] if "links" in package["data"] else []
|
||||
links_dict = {d["title"]: d["url"] for d in link_list}
|
||||
|
||||
if "author_id" not in package["author"] or "display_name" not in package["author"]:
|
||||
package["author"]["author_id"] = ""
|
||||
package["author"]["display_name"] = ""
|
||||
|
|
|
|||
|
|
@ -30,22 +30,22 @@ Item
|
|||
borderColor: printJob && printJob.configurationChanges.length !== 0 ? UM.Theme.getColor("warning") : UM.Theme.getColor("monitor_card_border")
|
||||
headerItem: Row
|
||||
{
|
||||
height: 48 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(48 * screenScaleFactor) // TODO: Theme!
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24 * screenScaleFactor // TODO: Theme!
|
||||
spacing: 18 * screenScaleFactor // TODO: Theme!
|
||||
anchors.leftMargin: Math.round(24 * screenScaleFactor) // TODO: Theme!
|
||||
spacing: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
|
||||
MonitorPrintJobPreview
|
||||
{
|
||||
printJob: base.printJob
|
||||
size: 32 * screenScaleFactor // TODO: Theme!
|
||||
size: Math.round(32 * screenScaleFactor) // TODO: Theme!
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
width: UM.Theme.getSize("monitor_column").width
|
||||
Rectangle
|
||||
{
|
||||
|
|
@ -74,7 +74,7 @@ Item
|
|||
Item
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
width: UM.Theme.getSize("monitor_column").width
|
||||
|
||||
Rectangle
|
||||
|
|
@ -95,7 +95,7 @@ Item
|
|||
visible: printJob
|
||||
|
||||
// FIXED-LINE-HEIGHT:
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
|
@ -104,13 +104,13 @@ Item
|
|||
Item
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 18 * screenScaleFactor // TODO: This should be childrenRect.height but QML throws warnings
|
||||
height: Math.round(18 * screenScaleFactor) // TODO: This should be childrenRect.height but QML throws warnings
|
||||
width: childrenRect.width
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("monitor_skeleton_loading")
|
||||
width: 72 * screenScaleFactor // TODO: Theme!
|
||||
width: Math.round(72 * screenScaleFactor) // TODO: Theme!
|
||||
height: parent.height
|
||||
visible: !printJob
|
||||
radius: 2 * screenScaleFactor // TODO: Theme!
|
||||
|
|
@ -124,21 +124,22 @@ Item
|
|||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
text: {
|
||||
if (printJob !== null) {
|
||||
if (printJob !== null)
|
||||
{
|
||||
if (printJob.assignedPrinter == null)
|
||||
{
|
||||
if (printJob.state == "error")
|
||||
{
|
||||
return catalog.i18nc("@label", "Unavailable printer")
|
||||
return catalog.i18nc("@label", "Unavailable printer");
|
||||
}
|
||||
return catalog.i18nc("@label", "First available")
|
||||
return catalog.i18nc("@label", "First available");
|
||||
}
|
||||
return printJob.assignedPrinter.name
|
||||
return printJob.assignedPrinter.name;
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
visible: printJob
|
||||
width: 120 * screenScaleFactor // TODO: Theme!
|
||||
width: Math.round(120 * screenScaleFactor) // TODO: Theme!
|
||||
|
||||
// FIXED-LINE-HEIGHT:
|
||||
height: parent.height
|
||||
|
|
@ -152,11 +153,11 @@ Item
|
|||
anchors
|
||||
{
|
||||
left: printerAssignmentLabel.right;
|
||||
leftMargin: 12 // TODO: Theme!
|
||||
leftMargin: Math.round(12 * screenScaleFactor) // TODO: Theme!
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
height: childrenRect.height
|
||||
spacing: 6 // TODO: Theme!
|
||||
spacing: Math.round(6 * screenScaleFactor) // TODO: Theme!
|
||||
visible: printJob
|
||||
|
||||
MonitorPrinterPill
|
||||
|
|
@ -171,10 +172,10 @@ Item
|
|||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
leftMargin: 74 * screenScaleFactor // TODO: Theme!
|
||||
leftMargin: Math.round(74 * screenScaleFactor) // TODO: Theme!
|
||||
}
|
||||
height: 108 * screenScaleFactor // TODO: Theme!
|
||||
spacing: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(108 * screenScaleFactor) // TODO: Theme!
|
||||
spacing: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
|
||||
MonitorPrinterConfiguration
|
||||
{
|
||||
|
|
@ -182,7 +183,7 @@ Item
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
buildplate: catalog.i18nc("@label", "Glass")
|
||||
configurations: base.printJob.configuration.extruderConfigurations
|
||||
height: 72 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(72 * screenScaleFactor) // TODO: Theme!
|
||||
}
|
||||
|
||||
Label {
|
||||
|
|
@ -193,7 +194,7 @@ Item
|
|||
anchors.top: printerConfiguration.top
|
||||
|
||||
// FIXED-LINE-HEIGHT:
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: Math.round(18 * screenScaleFactor) // TODO: Theme!
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
|
@ -206,21 +207,22 @@ Item
|
|||
anchors
|
||||
{
|
||||
right: parent.right
|
||||
rightMargin: 8 * screenScaleFactor // TODO: Theme!
|
||||
rightMargin: Math.round(8 * screenScaleFactor) // TODO: Theme!
|
||||
top: parent.top
|
||||
topMargin: 8 * screenScaleFactor // TODO: Theme!
|
||||
topMargin: Math.round(8 * screenScaleFactor) // TODO: Theme!
|
||||
}
|
||||
width: 32 * screenScaleFactor // TODO: Theme!
|
||||
height: 32 * screenScaleFactor // TODO: Theme!
|
||||
width: Math.round(32 * screenScaleFactor) // TODO: Theme!
|
||||
height: Math.round(32 * screenScaleFactor) // TODO: Theme!
|
||||
enabled: OutputDevice.supportsPrintJobActions
|
||||
onClicked: enabled ? contextMenu.switchPopupState() : {}
|
||||
visible:
|
||||
{
|
||||
if (!printJob) {
|
||||
return false
|
||||
if (!printJob)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var states = ["queued", "error", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"]
|
||||
return states.indexOf(printJob.state) !== -1
|
||||
var states = ["queued", "error", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"];
|
||||
return states.indexOf(printJob.state) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue