mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-06 05:23:58 -06:00
Merge branch 'master' into fixes_toolbox
This commit is contained in:
commit
1d6ba3ffb0
1432 changed files with 55649 additions and 45174 deletions
|
@ -419,13 +419,17 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if parser.has_option("metadata", "enabled"):
|
||||
extruder_info.enabled = parser["metadata"]["enabled"]
|
||||
if variant_id not in ("empty", "empty_variant"):
|
||||
extruder_info.variant_info = instance_container_info_dict[variant_id]
|
||||
if variant_id in instance_container_info_dict:
|
||||
extruder_info.variant_info = instance_container_info_dict[variant_id]
|
||||
|
||||
if material_id not in ("empty", "empty_material"):
|
||||
root_material_id = reverse_material_id_dict[material_id]
|
||||
extruder_info.root_material_id = root_material_id
|
||||
|
||||
definition_changes_id = parser["containers"][str(_ContainerIndexes.DefinitionChanges)]
|
||||
if definition_changes_id not in ("empty", "empty_definition_changes"):
|
||||
extruder_info.definition_changes_info = instance_container_info_dict[definition_changes_id]
|
||||
|
||||
user_changes_id = parser["containers"][str(_ContainerIndexes.UserChanges)]
|
||||
if user_changes_id not in ("empty", "empty_user_changes"):
|
||||
extruder_info.user_changes_info = instance_container_info_dict[user_changes_id]
|
||||
|
@ -905,6 +909,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
continue
|
||||
extruder_info = self._machine_info.extruder_info_dict[position]
|
||||
if extruder_info.variant_info is None:
|
||||
# If there is no variant_info, try to use the default variant. Otherwise, leave it be.
|
||||
node = variant_manager.getDefaultVariantNode(global_stack.definition, VariantType.NOZZLE, global_stack)
|
||||
if node is not None and node.getContainer() is not None:
|
||||
extruder_stack.variant = node.getContainer()
|
||||
continue
|
||||
parser = extruder_info.variant_info.parser
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._createSocket()
|
||||
|
||||
if self._process_layers_job is not None: # We were processing layers. Stop that, the layers are going to change soon.
|
||||
Logger.log("d", "Aborting process layers job...")
|
||||
Logger.log("i", "Aborting process layers job...")
|
||||
self._process_layers_job.abort()
|
||||
self._process_layers_job = None
|
||||
|
||||
|
@ -222,7 +222,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self) -> None:
|
||||
Logger.log("d", "Starting to slice...")
|
||||
Logger.log("i", "Starting to slice...")
|
||||
self._slice_start_time = time()
|
||||
if not self._build_plates_to_be_sliced:
|
||||
self.processingProgress.emit(1.0)
|
||||
|
@ -517,9 +517,6 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||
self.printDurationMessage.emit(source_build_plate_number, {}, [])
|
||||
self.processingProgress.emit(0.0)
|
||||
self.setState(BackendState.NotStarted)
|
||||
# if not self._use_timer:
|
||||
# With manually having to slice, we want to clear the old invalid layer data.
|
||||
self._clearLayerData(build_plate_changed)
|
||||
|
||||
self._invokeSlice()
|
||||
|
@ -563,10 +560,10 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
## Convenient function: mark everything to slice, emit state and clear layer data
|
||||
def needsSlicing(self) -> None:
|
||||
self.determineAutoSlicing()
|
||||
self.stopSlicing()
|
||||
self.markSliceAll()
|
||||
self.processingProgress.emit(0.0)
|
||||
self.setState(BackendState.NotStarted)
|
||||
if not self._use_timer:
|
||||
# With manually having to slice, we want to clear the old invalid layer data.
|
||||
self._clearLayerData()
|
||||
|
@ -735,6 +732,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
"support_interface": message.time_support_interface,
|
||||
"support": message.time_support,
|
||||
"skirt": message.time_skirt,
|
||||
"prime_tower": message.time_prime_tower,
|
||||
"travel": message.time_travel,
|
||||
"retract": message.time_retract,
|
||||
"none": message.time_none
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#Copyright (c) 2017 Ultimaker B.V.
|
||||
#Copyright (c) 2019 Ultimaker B.V.
|
||||
#Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import gc
|
||||
|
@ -136,23 +136,23 @@ class ProcessSlicedLayersJob(Job):
|
|||
|
||||
extruder = polygon.extruder
|
||||
|
||||
line_types = numpy.fromstring(polygon.line_type, dtype="u1") # Convert bytearray to numpy array
|
||||
line_types = numpy.fromstring(polygon.line_type, dtype = "u1") # Convert bytearray to numpy array
|
||||
|
||||
line_types = line_types.reshape((-1,1))
|
||||
|
||||
points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array
|
||||
points = numpy.fromstring(polygon.points, dtype = "f4") # Convert bytearray to numpy array
|
||||
if polygon.point_type == 0: # Point2D
|
||||
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
else: # Point3D
|
||||
points = points.reshape((-1,3))
|
||||
|
||||
line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # Convert bytearray to numpy array
|
||||
line_widths = numpy.fromstring(polygon.line_width, dtype = "f4") # Convert bytearray to numpy array
|
||||
line_widths = line_widths.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
|
||||
line_thicknesses = numpy.fromstring(polygon.line_thickness, dtype="f4") # Convert bytearray to numpy array
|
||||
line_thicknesses = numpy.fromstring(polygon.line_thickness, dtype = "f4") # Convert bytearray to numpy array
|
||||
line_thicknesses = line_thicknesses.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
|
||||
line_feedrates = numpy.fromstring(polygon.line_feedrate, dtype="f4") # Convert bytearray to numpy array
|
||||
line_feedrates = numpy.fromstring(polygon.line_feedrate, dtype = "f4") # Convert bytearray to numpy array
|
||||
line_feedrates = line_feedrates.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
|
||||
# Create a new 3D-array, copy the 2D points over and insert the right height.
|
||||
|
@ -194,7 +194,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
manager = ExtruderManager.getInstance()
|
||||
extruders = manager.getActiveExtruderStacks()
|
||||
if extruders:
|
||||
material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
|
||||
material_color_map = numpy.zeros((len(extruders), 4), dtype = numpy.float32)
|
||||
for extruder in extruders:
|
||||
position = int(extruder.getMetaDataEntry("position", default = "0"))
|
||||
try:
|
||||
|
@ -206,8 +206,8 @@ class ProcessSlicedLayersJob(Job):
|
|||
material_color_map[position, :] = color
|
||||
else:
|
||||
# Single extruder via global stack.
|
||||
material_color_map = numpy.zeros((1, 4), dtype=numpy.float32)
|
||||
color_code = global_container_stack.material.getMetaDataEntry("color_code", default="#e0e000")
|
||||
material_color_map = numpy.zeros((1, 4), dtype = numpy.float32)
|
||||
color_code = global_container_stack.material.getMetaDataEntry("color_code", default = "#e0e000")
|
||||
color = colorCodeToRGBA(color_code)
|
||||
material_color_map[0, :] = color
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ class StartSliceJob(Job):
|
|||
|
||||
for key in stack.getAllKeys():
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
|
||||
Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
|
||||
return True
|
||||
Job.yieldThread()
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.ReaderWriters.ProfileReader import ProfileReader
|
||||
|
||||
import zipfile
|
||||
|
@ -17,39 +20,43 @@ import zipfile
|
|||
class CuraProfileReader(ProfileReader):
|
||||
## Initialises the cura profile reader.
|
||||
# This does nothing since the only other function is basically stateless.
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
## Reads a cura profile from a file and returns it.
|
||||
#
|
||||
# \param file_name The file to read the cura profile from.
|
||||
# \return The cura profile that was in the file, if any. If the file could
|
||||
# not be read or didn't contain a valid profile, \code None \endcode is
|
||||
# \return The cura profiles that were in the file, if any. If the file
|
||||
# could not be read or didn't contain a valid profile, ``None`` is
|
||||
# returned.
|
||||
def read(self, file_name):
|
||||
def read(self, file_name: str) -> List[Optional[InstanceContainer]]:
|
||||
try:
|
||||
with zipfile.ZipFile(file_name, "r") as archive:
|
||||
results = []
|
||||
results = [] # type: List[Optional[InstanceContainer]]
|
||||
for profile_id in archive.namelist():
|
||||
with archive.open(profile_id) as f:
|
||||
serialized = f.read()
|
||||
profile = self._loadProfile(serialized.decode("utf-8"), profile_id)
|
||||
if profile is not None:
|
||||
results.append(profile)
|
||||
upgraded_profiles = self._upgradeProfile(serialized.decode("utf-8"), profile_id) #After upgrading it may split into multiple profiles.
|
||||
for upgraded_profile in upgraded_profiles:
|
||||
serialization, new_id = upgraded_profile
|
||||
profile = self._loadProfile(serialization, new_id)
|
||||
if profile is not None:
|
||||
results.append(profile)
|
||||
return results
|
||||
|
||||
except zipfile.BadZipFile:
|
||||
# It must be an older profile from Cura 2.1.
|
||||
with open(file_name, encoding = "utf-8") as fhandle:
|
||||
serialized = fhandle.read()
|
||||
return [self._loadProfile(serialized, profile_id) for serialized, profile_id in self._upgradeProfile(serialized, file_name)]
|
||||
serialized_bytes = fhandle.read()
|
||||
return [self._loadProfile(serialized, profile_id) for serialized, profile_id in self._upgradeProfile(serialized_bytes, file_name)]
|
||||
|
||||
## Convert a profile from an old Cura to this Cura if needed.
|
||||
#
|
||||
# \param serialized \type{str} The profile data to convert in the serialized on-disk format.
|
||||
# \param profile_id \type{str} The name of the profile.
|
||||
# \return \type{List[Tuple[str,str]]} List of serialized profile strings and matching profile names.
|
||||
def _upgradeProfile(self, serialized, profile_id):
|
||||
# \param serialized The profile data to convert in the serialized on-disk
|
||||
# format.
|
||||
# \param profile_id The name of the profile.
|
||||
# \return List of serialized profile strings and matching profile names.
|
||||
def _upgradeProfile(self, serialized: str, profile_id: str) -> List[Tuple[str, str]]:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
|
@ -61,18 +68,19 @@ class CuraProfileReader(ProfileReader):
|
|||
return []
|
||||
|
||||
version = int(parser["general"]["version"])
|
||||
setting_version = int(parser["metadata"].get("setting_version", "0"))
|
||||
if InstanceContainer.Version != version:
|
||||
name = parser["general"]["name"]
|
||||
return self._upgradeProfileVersion(serialized, name, version)
|
||||
return self._upgradeProfileVersion(serialized, name, version, setting_version)
|
||||
else:
|
||||
return [(serialized, profile_id)]
|
||||
|
||||
## Load a profile from a serialized string.
|
||||
#
|
||||
# \param serialized \type{str} The profile data to read.
|
||||
# \param profile_id \type{str} The name of the profile.
|
||||
# \return \type{InstanceContainer|None}
|
||||
def _loadProfile(self, serialized, profile_id):
|
||||
# \param serialized The profile data to read.
|
||||
# \param profile_id The name of the profile.
|
||||
# \return The profile that was stored in the string.
|
||||
def _loadProfile(self, serialized: str, profile_id: str) -> Optional[InstanceContainer]:
|
||||
# Create an empty profile.
|
||||
profile = InstanceContainer(profile_id)
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
|
@ -88,21 +96,31 @@ class CuraProfileReader(ProfileReader):
|
|||
|
||||
## Upgrade a serialized profile to the current profile format.
|
||||
#
|
||||
# \param serialized \type{str} The profile data to convert.
|
||||
# \param profile_id \type{str} The name of the profile.
|
||||
# \param source_version \type{int} The profile version of 'serialized'.
|
||||
# \return \type{List[Tuple[str,str]]} List of serialized profile strings and matching profile names.
|
||||
def _upgradeProfileVersion(self, serialized, profile_id, source_version):
|
||||
converter_plugins = PluginRegistry.getInstance().getAllMetaData(filter={"version_upgrade": {} }, active_only=True)
|
||||
# \param serialized The profile data to convert.
|
||||
# \param profile_id The name of the profile.
|
||||
# \param source_version The profile version of 'serialized'.
|
||||
# \return List of serialized profile strings and matching profile names.
|
||||
def _upgradeProfileVersion(self, serialized: str, profile_id: str, main_version: int, setting_version: int) -> List[Tuple[str, str]]:
|
||||
source_version = main_version * 1000000 + setting_version
|
||||
|
||||
source_format = ("profile", source_version)
|
||||
profile_convert_funcs = [plugin["version_upgrade"][source_format][2] for plugin in converter_plugins
|
||||
if source_format in plugin["version_upgrade"] and plugin["version_upgrade"][source_format][1] == InstanceContainer.Version]
|
||||
|
||||
if not profile_convert_funcs:
|
||||
from UM.VersionUpgradeManager import VersionUpgradeManager
|
||||
results = VersionUpgradeManager.getInstance().updateFilesData("quality_changes", source_version, [serialized], [profile_id])
|
||||
if results is None:
|
||||
return []
|
||||
|
||||
filenames, outputs = profile_convert_funcs[0](serialized, profile_id)
|
||||
if filenames is None and outputs is None:
|
||||
serialized = results.files_data[0]
|
||||
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
if "general" not in parser:
|
||||
Logger.log("w", "Missing required section 'general'.")
|
||||
return []
|
||||
return list(zip(outputs, filenames))
|
||||
|
||||
new_source_version = results.version
|
||||
if int(new_source_version / 1000000) != InstanceContainer.Version or new_source_version % 1000000 != CuraApplication.SettingVersion:
|
||||
Logger.log("e", "Failed to upgrade profile [%s]", profile_id)
|
||||
|
||||
if int(parser["general"]["version"]) != InstanceContainer.Version:
|
||||
Logger.log("e", "Failed to upgrade profile [%s]", profile_id)
|
||||
return []
|
||||
return [(serialized, profile_id)]
|
||||
|
|
|
@ -104,7 +104,7 @@ class FirmwareUpdateCheckerJob(Job):
|
|||
# because the new version of Cura will be release before the firmware and we don't want to
|
||||
# notify the user when no new firmware version is available.
|
||||
if (checked_version != "") and (checked_version != current_version):
|
||||
Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE")
|
||||
Logger.log("i", "Showing firmware update message for new version: {version}".format(current_version))
|
||||
message = FirmwareUpdateCheckerMessage(machine_id, self._machine_name,
|
||||
self._lookups.getRedirectUserUrl())
|
||||
message.actionTriggered.connect(self._callback)
|
||||
|
|
|
@ -371,7 +371,7 @@ class FlavorParser:
|
|||
elif type == "SUPPORT-INTERFACE":
|
||||
self._layer_type = LayerPolygon.SupportInterfaceType
|
||||
elif type == "PRIME-TOWER":
|
||||
self._layer_type = LayerPolygon.SkirtType
|
||||
self._layer_type = LayerPolygon.PrimeTowerType
|
||||
else:
|
||||
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ Item
|
|||
|
||||
property int labelWidth: 210 * screenScaleFactor
|
||||
property int controlWidth: (UM.Theme.getSize("setting_control").width * 3 / 4) | 0
|
||||
property var labelFont: UM.Theme.getFont("medium")
|
||||
property var labelFont: UM.Theme.getFont("default")
|
||||
|
||||
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
||||
property int columnSpacing: 3 * screenScaleFactor
|
||||
property int propertyStoreIndex: manager.storeContainerIndex // definition_changes
|
||||
property int propertyStoreIndex: manager ? manager.storeContainerIndex : 1 // definition_changes
|
||||
|
||||
property string extruderStackId: ""
|
||||
property int extruderPosition: 0
|
||||
|
@ -107,6 +107,7 @@ Item
|
|||
labelWidth: base.labelWidth
|
||||
controlWidth: base.controlWidth
|
||||
unitText: catalog.i18nc("@label", "mm")
|
||||
allowNegativeValue: true
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
||||
|
@ -121,6 +122,7 @@ Item
|
|||
labelWidth: base.labelWidth
|
||||
controlWidth: base.controlWidth
|
||||
unitText: catalog.i18nc("@label", "mm")
|
||||
allowNegativeValue: true
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@ Item
|
|||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
|
||||
property int labelWidth: 120 * screenScaleFactor
|
||||
property int controlWidth: (UM.Theme.getSize("setting_control").width * 3 / 4) | 0
|
||||
property var labelFont: UM.Theme.getFont("default")
|
||||
|
||||
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
||||
property int columnSpacing: 3 * screenScaleFactor
|
||||
property int propertyStoreIndex: manager.storeContainerIndex // definition_changes
|
||||
property int propertyStoreIndex: manager ? manager.storeContainerIndex : 1 // definition_changes
|
||||
|
||||
property int labelWidth: (columnWidth * 2 / 3 - UM.Theme.getSize("default_margin").width * 2) | 0
|
||||
property int controlWidth: (columnWidth / 3) | 0
|
||||
property var labelFont: UM.Theme.getFont("default")
|
||||
|
||||
property string machineStackId: Cura.MachineManager.activeMachineId
|
||||
|
||||
|
@ -59,6 +59,8 @@ Item
|
|||
font: UM.Theme.getFont("medium_bold")
|
||||
color: UM.Theme.getColor("text")
|
||||
renderType: Text.NativeRendering
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Cura.NumericTextFieldWithUnit // "X (Width)"
|
||||
|
@ -175,6 +177,8 @@ Item
|
|||
font: UM.Theme.getFont("medium_bold")
|
||||
color: UM.Theme.getColor("text")
|
||||
renderType: Text.NativeRendering
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Cura.PrintHeadMinMaxTextField // "X min"
|
||||
|
@ -191,6 +195,7 @@ Item
|
|||
|
||||
axisName: "x"
|
||||
axisMinOrMax: "min"
|
||||
allowNegativeValue: true
|
||||
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
@ -209,6 +214,7 @@ Item
|
|||
|
||||
axisName: "y"
|
||||
axisMinOrMax: "min"
|
||||
allowNegativeValue: true
|
||||
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
@ -227,6 +233,7 @@ Item
|
|||
|
||||
axisName: "x"
|
||||
axisMinOrMax: "max"
|
||||
allowNegativeValue: true
|
||||
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
@ -247,6 +254,7 @@ Item
|
|||
|
||||
axisName: "y"
|
||||
axisMinOrMax: "max"
|
||||
allowNegativeValue: true
|
||||
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ Rectangle
|
|||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
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.")
|
||||
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")
|
||||
wrapMode: Text.WordWrap
|
||||
|
@ -166,4 +166,4 @@ Rectangle
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ Item {
|
|||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
containerId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.definition.id: ""
|
||||
expanded: [ "*" ]
|
||||
filter:
|
||||
{
|
||||
|
@ -467,7 +467,7 @@ Item {
|
|||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
id: definitionsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
containerId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.definition.id: ""
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
|
||||
expanded: [ "*" ]
|
||||
exclude:
|
||||
|
|
|
@ -219,6 +219,7 @@ class PostProcessingPlugin(QObject, Extension):
|
|||
self._script_list.clear()
|
||||
if not new_stack.getMetaDataEntry("post_processing_scripts"): # Missing or empty.
|
||||
self.scriptListChanged.emit() # Even emit this if it didn't change. We want it to write the empty list to the stack's metadata.
|
||||
self.setSelectedScriptIndex(-1)
|
||||
return
|
||||
|
||||
self._script_list.clear()
|
||||
|
|
|
@ -100,8 +100,8 @@ class ChangeAtZ(Script):
|
|||
},
|
||||
"d_twLayers":
|
||||
{
|
||||
"label": "No. Layers",
|
||||
"description": "No. of layers used to change",
|
||||
"label": "Layer Spread",
|
||||
"description": "The change will be gradual over this many layers. Enter 1 to make the change immediate.",
|
||||
"unit": "",
|
||||
"type": "int",
|
||||
"default_value": 1,
|
||||
|
@ -330,7 +330,7 @@ class ChangeAtZ(Script):
|
|||
"extruderOne": self.getSettingValueByKey("i2_extruderOne"),
|
||||
"extruderTwo": self.getSettingValueByKey("i4_extruderTwo"),
|
||||
"fanSpeed": self.getSettingValueByKey("j2_fanSpeed")}
|
||||
old = {"speed": -1, "flowrate": -1, "flowrateOne": -1, "flowrateTwo": -1, "platformTemp": -1, "extruderOne": -1,
|
||||
old = {"speed": -1, "flowrate": 100, "flowrateOne": -1, "flowrateTwo": -1, "platformTemp": -1, "extruderOne": -1,
|
||||
"extruderTwo": -1, "bedTemp": -1, "fanSpeed": -1, "state": -1}
|
||||
twLayers = self.getSettingValueByKey("d_twLayers")
|
||||
if self.getSettingValueByKey("c_behavior") == "single_layer":
|
||||
|
@ -410,6 +410,8 @@ class ChangeAtZ(Script):
|
|||
tmp_extruder = self.getValue(line, "T", None)
|
||||
if tmp_extruder == None: #check if extruder is specified
|
||||
old["flowrate"] = self.getValue(line, "S", old["flowrate"])
|
||||
if old["flowrate"] == -1:
|
||||
old["flowrate"] = 100.0
|
||||
elif tmp_extruder == 0: #first extruder
|
||||
old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"])
|
||||
elif tmp_extruder == 1: #second extruder
|
||||
|
@ -481,9 +483,9 @@ class ChangeAtZ(Script):
|
|||
state = 2
|
||||
done_layers = 0
|
||||
if targetL_i > -100000:
|
||||
modified_gcode += ";ChangeAtZ V%s: reset below Layer %d\n" % (self.version,targetL_i)
|
||||
modified_gcode += ";ChangeAtZ V%s: reset below Layer %d\n" % (self.version, targetL_i)
|
||||
else:
|
||||
modified_gcode += ";ChangeAtZ V%s: reset below %1.2f mm\n" % (self.version,targetZ)
|
||||
modified_gcode += ";ChangeAtZ V%s: reset below %1.2f mm\n" % (self.version, targetZ)
|
||||
if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting
|
||||
modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1)
|
||||
else: #executes on RepRap, UM2 with Ultigcode and Cura setting
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from UM.Logger import Logger
|
||||
from typing import List
|
||||
from ..Script import Script
|
||||
|
||||
class FilamentChange(Script):
|
||||
|
@ -65,9 +63,10 @@ class FilamentChange(Script):
|
|||
}
|
||||
}"""
|
||||
|
||||
def execute(self, data: list):
|
||||
|
||||
"""data is a list. Each index contains a layer"""
|
||||
## Inserts the filament change g-code at specific layer numbers.
|
||||
# \param data A list of layers of g-code.
|
||||
# \return A similar list, with filament change commands inserted.
|
||||
def execute(self, data: List[str]):
|
||||
layer_nums = self.getSettingValueByKey("layer_number")
|
||||
initial_retract = self.getSettingValueByKey("initial_retract")
|
||||
later_retract = self.getSettingValueByKey("later_retract")
|
||||
|
@ -88,32 +87,16 @@ class FilamentChange(Script):
|
|||
if y_pos is not None:
|
||||
color_change = color_change + (" Y%.2f" % y_pos)
|
||||
|
||||
color_change = color_change + " ; Generated by FilamentChange plugin"
|
||||
color_change = color_change + " ; Generated by FilamentChange plugin\n"
|
||||
|
||||
layer_targets = layer_nums.split(",")
|
||||
if len(layer_targets) > 0:
|
||||
for layer_num in layer_targets:
|
||||
layer_num = int(layer_num.strip())
|
||||
if layer_num <= len(data):
|
||||
index, layer_data = self._searchLayerData(data, layer_num - 1)
|
||||
if layer_data is None:
|
||||
Logger.log("e", "Could not find the layer {layer_num}".format(layer_num = layer_num))
|
||||
continue
|
||||
lines = layer_data.split("\n")
|
||||
lines.insert(2, color_change)
|
||||
final_line = "\n".join(lines)
|
||||
data[index] = final_line
|
||||
try:
|
||||
layer_num = int(layer_num.strip()) + 1 #Needs +1 because the 1st layer is reserved for start g-code.
|
||||
except ValueError: #Layer number is not an integer.
|
||||
continue
|
||||
if 0 < layer_num < len(data):
|
||||
data[layer_num] = color_change + data[layer_num]
|
||||
|
||||
return data
|
||||
|
||||
## This method returns the data corresponding with the indicated layer number, looking in the gcode for
|
||||
# the occurrence of this layer number.
|
||||
def _searchLayerData(self, data: list, layer_num: int) -> Tuple[int, Optional[str]]:
|
||||
for index, layer_data in enumerate(data):
|
||||
first_line = layer_data.split("\n")[0]
|
||||
# The first line should contain the layer number at the beginning.
|
||||
if first_line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||
# If found the layer that we are looking for, then return the data
|
||||
if first_line[len(self._layer_keyword):] == str(layer_num):
|
||||
return index, layer_data
|
||||
return 0, None
|
||||
return data
|
|
@ -145,6 +145,7 @@ class Stretcher():
|
|||
current.readStep(line)
|
||||
onestep = GCodeStep(-1, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
onestep.comment = line
|
||||
else:
|
||||
onestep = GCodeStep(-1, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
|
|
|
@ -572,14 +572,14 @@ class SimulationView(CuraView):
|
|||
self._current_layer_jumps = job.getResult().get("jumps")
|
||||
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
|
||||
|
||||
self._top_layers_job = None # type: Optional["_CreateTopLayersJob"]
|
||||
self._top_layers_job = None
|
||||
|
||||
def _updateWithPreferences(self) -> None:
|
||||
self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count"))
|
||||
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
||||
self._compatibility_mode = self._evaluateCompatibilityMode()
|
||||
|
||||
self.setSimulationViewType(int(float(Application.getInstance().getPreferences().getValue("layerview/layer_view_type"))));
|
||||
self.setSimulationViewType(int(float(Application.getInstance().getPreferences().getValue("layerview/layer_view_type"))))
|
||||
|
||||
for extruder_nr, extruder_opacity in enumerate(Application.getInstance().getPreferences().getValue("layerview/extruder_opacities").split("|")):
|
||||
try:
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[shaders]
|
||||
vertex =
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp float u_shade_factor;
|
||||
uniform highp int u_layer_view_type;
|
||||
|
@ -16,7 +19,7 @@ vertex =
|
|||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
// shade the color depending on the extruder index
|
||||
v_color = a_color;
|
||||
// 8 and 9 are travel moves
|
||||
|
@ -76,7 +79,10 @@ fragment =
|
|||
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp float u_shade_factor;
|
||||
uniform highp int u_layer_view_type;
|
||||
|
@ -92,7 +98,7 @@ vertex41core =
|
|||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
v_color = a_color;
|
||||
if ((a_line_type != 8) && (a_line_type != 9)) {
|
||||
v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
|
||||
|
@ -154,7 +160,9 @@ u_show_skin = 1
|
|||
u_show_infill = 1
|
||||
|
||||
[bindings]
|
||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewMatrix = view_matrix
|
||||
u_projectionMatrix = projection_matrix
|
||||
|
||||
[attributes]
|
||||
a_vertex = vertex
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
[shaders]
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp float u_max_feedrate;
|
||||
uniform lowp float u_min_feedrate;
|
||||
|
@ -104,7 +104,10 @@ vertex41core =
|
|||
geometry41core =
|
||||
#version 410
|
||||
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform int u_show_travel_moves;
|
||||
uniform int u_show_helpers;
|
||||
uniform int u_show_skin;
|
||||
|
@ -136,6 +139,8 @@ geometry41core =
|
|||
|
||||
void main()
|
||||
{
|
||||
highp mat4 viewProjectionMatrix = u_projectionMatrix * u_viewMatrix;
|
||||
|
||||
vec4 g_vertex_delta;
|
||||
vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
||||
vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
|
||||
|
@ -183,65 +188,83 @@ geometry41core =
|
|||
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
|
||||
|
||||
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
|
||||
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert);
|
||||
vec4 va_up = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 va_down = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert);
|
||||
vec4 vb_down = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 vb_up = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
|
||||
// Travels: flat plane with pointy ends
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_down);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_head);
|
||||
//And reverse so that the line is also visible from the back side.
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
|
||||
EndPrimitive();
|
||||
} else {
|
||||
vec4 va_m_horz = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz);
|
||||
vec4 vb_m_horz = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz);
|
||||
vec4 va_p_vert = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert);
|
||||
vec4 vb_p_vert = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert);
|
||||
vec4 va_p_horz = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
|
||||
vec4 vb_p_horz = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
|
||||
vec4 va_m_vert = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert);
|
||||
vec4 vb_m_vert = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
|
||||
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head);
|
||||
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head);
|
||||
|
||||
// All normal lines are rendered as 3d tubes.
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_p_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_p_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, va_m_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, vb_m_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
// left side
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_p_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, va_m_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
// right side
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_p_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, vb_head);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, vb_m_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, vb_head);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
|
@ -301,9 +324,9 @@ u_min_thickness = 0
|
|||
u_max_thickness = 1
|
||||
|
||||
[bindings]
|
||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewProjectionMatrix = view_projection_matrix
|
||||
u_viewMatrix = view_matrix
|
||||
u_projectionMatrix = projection_matrix
|
||||
u_normalMatrix = normal_matrix
|
||||
u_lightPosition = light_0_position
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
[shaders]
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
|
||||
|
||||
|
@ -58,7 +58,10 @@ vertex41core =
|
|||
geometry41core =
|
||||
#version 410
|
||||
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform int u_show_travel_moves;
|
||||
uniform int u_show_helpers;
|
||||
uniform int u_show_skin;
|
||||
|
@ -90,6 +93,8 @@ geometry41core =
|
|||
|
||||
void main()
|
||||
{
|
||||
highp mat4 viewProjectionMatrix = u_projectionMatrix * u_viewMatrix;
|
||||
|
||||
vec4 g_vertex_delta;
|
||||
vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
||||
vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
|
||||
|
@ -137,65 +142,83 @@ geometry41core =
|
|||
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
|
||||
|
||||
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
|
||||
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert);
|
||||
vec4 va_up = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 va_down = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert);
|
||||
vec4 vb_down = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
vec4 vb_up = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert);
|
||||
|
||||
// Travels: flat plane with pointy ends
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_down);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_head);
|
||||
//And reverse so that the line is also visible from the back side.
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_up);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_down);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_up);
|
||||
|
||||
EndPrimitive();
|
||||
} else {
|
||||
vec4 va_m_horz = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz);
|
||||
vec4 vb_m_horz = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz);
|
||||
vec4 va_p_vert = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert);
|
||||
vec4 vb_p_vert = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert);
|
||||
vec4 va_p_horz = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
|
||||
vec4 vb_p_horz = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
|
||||
vec4 va_m_vert = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert);
|
||||
vec4 vb_m_vert = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
|
||||
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head);
|
||||
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head);
|
||||
|
||||
// All normal lines are rendered as 3d tubes.
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_p_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_p_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, va_m_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, vb_m_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
// left side
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, va_p_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, va_p_horz);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, va_m_vert);
|
||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, va_head);
|
||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
// right side
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, vb_p_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, vb_head);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, vb_m_horz);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, vb_m_vert);
|
||||
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, vb_head);
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, vb_p_horz);
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
|
@ -246,9 +269,9 @@ u_show_skin = 1
|
|||
u_show_infill = 1
|
||||
|
||||
[bindings]
|
||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewProjectionMatrix = view_projection_matrix
|
||||
u_viewMatrix = view_matrix
|
||||
u_projectionMatrix = projection_matrix
|
||||
u_normalMatrix = normal_matrix
|
||||
u_lightPosition = light_0_position
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[shaders]
|
||||
vertex =
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp float u_shade_factor;
|
||||
uniform highp int u_layer_view_type;
|
||||
|
@ -16,7 +19,7 @@ vertex =
|
|||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
// shade the color depending on the extruder index
|
||||
v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer;
|
||||
// 8 and 9 are travel moves
|
||||
|
@ -80,7 +83,10 @@ fragment =
|
|||
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
uniform lowp float u_active_extruder;
|
||||
uniform lowp float u_shade_factor;
|
||||
uniform highp int u_layer_view_type;
|
||||
|
@ -96,7 +102,7 @@ vertex41core =
|
|||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer
|
||||
// if ((a_line_type != 8) && (a_line_type != 9)) {
|
||||
// v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
|
||||
|
@ -159,7 +165,9 @@ u_show_skin = 1
|
|||
u_show_infill = 1
|
||||
|
||||
[bindings]
|
||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewMatrix = view_matrix
|
||||
u_projectionMatrix = projection_matrix
|
||||
|
||||
[attributes]
|
||||
a_vertex = vertex
|
||||
|
|
|
@ -126,6 +126,8 @@ class SliceInfo(QObject, Extension):
|
|||
else:
|
||||
data["active_mode"] = "custom"
|
||||
|
||||
data["camera_view"] = application.getPreferences().getValue("general/camera_perspective_mode")
|
||||
|
||||
definition_changes = global_stack.definitionChanges
|
||||
machine_settings_changed_by_user = False
|
||||
if definition_changes.getId() != "empty":
|
||||
|
|
|
@ -98,8 +98,10 @@ class SupportEraser(Tool):
|
|||
|
||||
node.setName("Eraser")
|
||||
node.setSelectable(True)
|
||||
node.setCalculateBoundingBox(True)
|
||||
mesh = self._createCube(10)
|
||||
node.setMeshData(mesh.build())
|
||||
node.calculateBoundingBoxMesh()
|
||||
|
||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
node.addDecorator(BuildPlateDecorator(active_build_plate))
|
||||
|
|
|
@ -109,6 +109,8 @@ Item
|
|||
top: description.bottom
|
||||
left: properties.right
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
right: parent.right
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
|
@ -123,6 +125,8 @@ Item
|
|||
}
|
||||
return ""
|
||||
}
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
|
|
|
@ -89,6 +89,7 @@ Item
|
|||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Your rating") + ":"
|
||||
visible: details.type == "plugin"
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
renderType: Text.NativeRendering
|
||||
|
|
|
@ -48,7 +48,6 @@ Item
|
|||
{
|
||||
text: model.name
|
||||
width: parent.width
|
||||
height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
|
||||
wrapMode: Text.WordWrap
|
||||
font: UM.Theme.getFont("large_bold")
|
||||
color: pluginInfo.color
|
||||
|
|
|
@ -278,7 +278,7 @@ class Toolbox(QObject, Extension):
|
|||
for plugin_id in old_plugin_ids:
|
||||
# Neither the installed packages nor the packages that are scheduled to remove are old plugins
|
||||
if plugin_id not in installed_package_ids and plugin_id not in scheduled_to_remove_package_ids:
|
||||
Logger.log("i", "Found a plugin that was installed with the old plugin browser: %s", plugin_id)
|
||||
Logger.log("d", "Found a plugin that was installed with the old plugin browser: %s", plugin_id)
|
||||
|
||||
old_metadata = self._plugin_registry.getMetaData(plugin_id)
|
||||
new_metadata = self._convertPluginMetadata(old_metadata)
|
||||
|
@ -526,7 +526,7 @@ class Toolbox(QObject, Extension):
|
|||
# Make API Calls
|
||||
# --------------------------------------------------------------------------
|
||||
def _makeRequestByType(self, request_type: str) -> None:
|
||||
Logger.log("i", "Requesting %s metadata from server.", request_type)
|
||||
Logger.log("d", "Requesting %s metadata from server.", request_type)
|
||||
request = QNetworkRequest(self._request_urls[request_type])
|
||||
for header_name, header_value in self._request_headers:
|
||||
request.setRawHeader(header_name, header_value)
|
||||
|
|
|
@ -78,7 +78,7 @@ Cura.MachineAction
|
|||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
renderType: Text.NativeRendering
|
||||
text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:")
|
||||
text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.") + "\n\n" + catalog.i18nc("@label", "Select your printer from the list below:")
|
||||
}
|
||||
|
||||
Row
|
||||
|
|
|
@ -30,6 +30,26 @@ UM.Dialog
|
|||
OutputDevice.forceSendJob(printer.activePrintJob.key)
|
||||
overrideConfirmationDialog.close()
|
||||
}
|
||||
visible:
|
||||
{
|
||||
// Don't show the button if we're missing a printer or print job
|
||||
if (!printer || !printer.activePrintJob)
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
// Check each required change...
|
||||
for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++)
|
||||
{
|
||||
var change = printer.activePrintJob.configurationChanges[i]
|
||||
// If that type of change is in the list of blocking changes, hide the button
|
||||
if (!change.canOverride)
|
||||
{
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
Button
|
||||
{
|
||||
|
@ -140,4 +160,4 @@ UM.Dialog
|
|||
}
|
||||
return translationText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ Item
|
|||
enabled: visible && !(printJob.state == "pausing" || printJob.state == "resuming");
|
||||
onClicked: {
|
||||
if (printJob.state == "paused") {
|
||||
printJob.setState("print");
|
||||
printJob.setState("resume");
|
||||
popUp.close();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,10 +22,6 @@ Item
|
|||
// The print job which all other data is derived from
|
||||
property var printJob: null
|
||||
|
||||
// If the printer is a cloud printer or not. Other items base their enabled state off of this boolean. In the future
|
||||
// they might not need to though.
|
||||
property bool cloudConnection: Cura.MachineManager.activeMachineIsUsingCloudConnection
|
||||
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
|
@ -51,7 +47,7 @@ Item
|
|||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
|
||||
width: UM.Theme.getSize("monitor_column").width
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("monitor_skeleton_loading")
|
||||
|
@ -79,7 +75,7 @@ Item
|
|||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
|
||||
width: UM.Theme.getSize("monitor_column").width
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("monitor_skeleton_loading")
|
||||
|
@ -217,7 +213,7 @@ Item
|
|||
}
|
||||
width: 32 * screenScaleFactor // TODO: Theme!
|
||||
height: 32 * screenScaleFactor // TODO: Theme!
|
||||
enabled: !cloudConnection
|
||||
enabled: OutputDevice.supportsPrintJobActions
|
||||
onClicked: enabled ? contextMenu.switchPopupState() : {}
|
||||
visible:
|
||||
{
|
||||
|
@ -250,7 +246,7 @@ Item
|
|||
MonitorInfoBlurb
|
||||
{
|
||||
id: contextMenuDisabledInfo
|
||||
text: catalog.i18nc("@info", "These options are not available because you are monitoring a cloud printer.")
|
||||
text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
|
||||
target: contextMenuButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,12 @@ Item
|
|||
anchors
|
||||
{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
value: printJob ? printJob.progress : 0
|
||||
width: UM.Theme.getSize("monitor_column").width
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: percentLabel
|
||||
|
@ -38,6 +41,7 @@ Item
|
|||
{
|
||||
left: progressBar.right
|
||||
leftMargin: 18 * screenScaleFactor // TODO: Theme!
|
||||
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")
|
||||
|
@ -56,6 +60,7 @@ Item
|
|||
{
|
||||
left: percentLabel.right
|
||||
leftMargin: 18 * screenScaleFactor // TODO: Theme!
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: UM.Theme.getColor("monitor_text_primary")
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
|
|
|
@ -172,8 +172,7 @@ Item
|
|||
}
|
||||
width: 36 * screenScaleFactor // TODO: Theme!
|
||||
height: 36 * screenScaleFactor // TODO: Theme!
|
||||
enabled: !cloudConnection
|
||||
|
||||
enabled: OutputDevice.supportsPrintJobActions
|
||||
onClicked: enabled ? contextMenu.switchPopupState() : {}
|
||||
visible:
|
||||
{
|
||||
|
@ -206,7 +205,7 @@ Item
|
|||
MonitorInfoBlurb
|
||||
{
|
||||
id: contextMenuDisabledInfo
|
||||
text: catalog.i18nc("@info", "These options are not available because you are monitoring a cloud printer.")
|
||||
text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
|
||||
target: contextMenuButton
|
||||
}
|
||||
|
||||
|
@ -244,7 +243,6 @@ Item
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Divider
|
||||
Rectangle
|
||||
{
|
||||
|
|
|
@ -42,7 +42,6 @@ Item
|
|||
}
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
width: childrenRect.width
|
||||
visible: !cloudConnection
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
|
@ -65,7 +64,7 @@ Item
|
|||
color: UM.Theme.getColor("monitor_text_link")
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
linkColor: UM.Theme.getColor("monitor_text_link")
|
||||
text: catalog.i18nc("@label link to connect manager", "Go to Cura Connect")
|
||||
text: catalog.i18nc("@label link to connect manager", "Manage in browser")
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +72,7 @@ Item
|
|||
MouseArea
|
||||
{
|
||||
anchors.fill: manageQueueLabel
|
||||
enabled: !cloudConnection
|
||||
hoverEnabled: !cloudConnection
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
|
||||
onClicked: OutputDevice.openPrintJobControlPanel()
|
||||
onEntered:
|
||||
{
|
||||
manageQueueText.font.underline = true
|
||||
|
@ -98,6 +95,22 @@ Item
|
|||
}
|
||||
spacing: 18 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
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
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 600 * 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
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Print jobs")
|
||||
|
@ -111,6 +124,7 @@ Item
|
|||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
visible: printJobList.count > 0
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -120,12 +134,13 @@ Item
|
|||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
|
||||
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
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -135,12 +150,13 @@ Item
|
|||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,89 +200,4 @@ Item
|
|||
spacing: 6 // TODO: Theme!
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors
|
||||
{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: printJobQueueHeadings.bottom
|
||||
topMargin: 12 * screenScaleFactor // TODO: Theme!
|
||||
}
|
||||
height: 48 * screenScaleFactor // TODO: Theme!
|
||||
width: parent.width
|
||||
color: UM.Theme.getColor("monitor_card_background")
|
||||
border.color: UM.Theme.getColor("monitor_card_border")
|
||||
radius: 2 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
visible: printJobList.model.length == 0
|
||||
|
||||
Row
|
||||
{
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
leftMargin: 18 * screenScaleFactor // TODO: Theme!
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
spacing: 18 * screenScaleFactor // TODO: Theme!
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
|
||||
Label
|
||||
{
|
||||
text: i18n.i18nc("@info", "All jobs are printed.")
|
||||
color: UM.Theme.getColor("monitor_text_primary")
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: viewPrintHistoryLabel
|
||||
|
||||
height: 18 * screenScaleFactor // TODO: Theme!
|
||||
width: childrenRect.width
|
||||
visible: !cloudConnection
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: printHistoryIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: UM.Theme.getColor("monitor_text_link")
|
||||
source: UM.Theme.getIcon("external_link")
|
||||
width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
|
||||
height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: viewPrintHistoryText
|
||||
anchors
|
||||
{
|
||||
left: printHistoryIcon.right
|
||||
leftMargin: 6 * screenScaleFactor // TODO: Theme!
|
||||
verticalCenter: printHistoryIcon.verticalCenter
|
||||
}
|
||||
color: UM.Theme.getColor("monitor_text_link")
|
||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||
linkColor: UM.Theme.getColor("monitor_text_link")
|
||||
text: catalog.i18nc("@label link to connect manager", "View print history")
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
|
||||
onEntered:
|
||||
{
|
||||
viewPrintHistoryText.font.underline = true
|
||||
}
|
||||
onExited:
|
||||
{
|
||||
viewPrintHistoryText.font.underline = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,21 @@ class CloudApiClient:
|
|||
reply = self._manager.post(self._createEmptyRequest(url), b"")
|
||||
self._addCallback(reply, on_finished, CloudPrintResponse)
|
||||
|
||||
## Send a print job action to the cluster for the given print job.
|
||||
# \param cluster_id: The ID of the cluster.
|
||||
# \param cluster_job_id: The ID of the print job within the cluster.
|
||||
# \param action: The name of the action to execute.
|
||||
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
body = b""
|
||||
if data:
|
||||
try:
|
||||
body = json.dumps({"data": data}).encode()
|
||||
except JSONDecodeError as err:
|
||||
Logger.log("w", "Could not encode body: %s", err)
|
||||
return
|
||||
url = "{}/clusters/{}/print_jobs/{}/action/{}".format(self.CLUSTER_API_ROOT, cluster_id, cluster_job_id, action)
|
||||
self._manager.post(self._createEmptyRequest(url), body)
|
||||
|
||||
## We override _createEmptyRequest in order to add the user credentials.
|
||||
# \param url: The URL to request
|
||||
# \param content_type: The type of the body contents.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
|
||||
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
@ -13,10 +14,13 @@ class CloudOutputController(PrinterOutputController):
|
|||
|
||||
# The cloud connection only supports fetching the printer and queue status and adding a job to the queue.
|
||||
# To let the UI know this we mark all features below as False.
|
||||
self.can_pause = False
|
||||
self.can_abort = False
|
||||
self.can_pause = True
|
||||
self.can_abort = True
|
||||
self.can_pre_heat_bed = False
|
||||
self.can_pre_heat_hotends = False
|
||||
self.can_send_raw_gcode = False
|
||||
self.can_control_manually = False
|
||||
self.can_update_firmware = False
|
||||
|
||||
def setJobState(self, job: "PrintJobOutputModel", state: str):
|
||||
self._output_device.setJobState(job.key, state)
|
||||
|
|
|
@ -6,6 +6,7 @@ from time import time
|
|||
from typing import Dict, List, Optional, Set, cast
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Backend.Backend import BackendState
|
||||
|
@ -15,6 +16,7 @@ from UM.Message import Message
|
|||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Qt.Duration import Duration, DurationFormat
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Version import Version
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice
|
||||
|
@ -33,8 +35,7 @@ from .Models.CloudPrintResponse import CloudPrintResponse
|
|||
from .Models.CloudPrintJobResponse import CloudPrintJobResponse
|
||||
from .Models.CloudClusterPrinterStatus import CloudClusterPrinterStatus
|
||||
from .Models.CloudClusterPrintJobStatus import CloudClusterPrintJobStatus
|
||||
from .Utils import findChanges, formatDateCompleted, formatTimeCompleted
|
||||
|
||||
from .Utils import formatDateCompleted, formatTimeCompleted
|
||||
|
||||
I18N_CATALOG = i18nCatalog("cura")
|
||||
|
||||
|
@ -44,10 +45,12 @@ I18N_CATALOG = i18nCatalog("cura")
|
|||
# As such, those methods have been implemented here.
|
||||
# Note that this device represents a single remote cluster, not a list of multiple clusters.
|
||||
class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
# The interval with which the remote clusters are checked
|
||||
CHECK_CLUSTER_INTERVAL = 10.0 # seconds
|
||||
|
||||
# The minimum version of firmware that support print job actions over cloud.
|
||||
PRINT_JOB_ACTIONS_MIN_VERSION = Version("5.3.0")
|
||||
|
||||
# Signal triggered when the print jobs in the queue were changed.
|
||||
printJobsChanged = pyqtSignal()
|
||||
|
||||
|
@ -58,14 +61,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
# Therefore we create a private signal used to trigger the printersChanged signal.
|
||||
_clusterPrintersChanged = pyqtSignal()
|
||||
|
||||
# Map of Cura Connect machine_variant field to Cura machine types.
|
||||
# Needed for printer discovery stack creation.
|
||||
_host_machine_variant_to_machine_type_map = {
|
||||
"Ultimaker 3": "ultimaker3",
|
||||
"Ultimaker 3 Extended": "ultimaker3_extended",
|
||||
"Ultimaker S5": "ultimaker_s5"
|
||||
}
|
||||
|
||||
## Creates a new cloud output device
|
||||
# \param api_client: The client that will run the API calls
|
||||
# \param cluster: The device response received from the cloud API.
|
||||
|
@ -79,11 +74,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
b"address": cluster.host_internal_ip.encode() if cluster.host_internal_ip else b"",
|
||||
b"name": cluster.friendly_name.encode() if cluster.friendly_name else b"",
|
||||
b"firmware_version": cluster.host_version.encode() if cluster.host_version else b"",
|
||||
b"printer_type": cluster.printer_type.encode() if cluster.printer_type else b"",
|
||||
b"cluster_size": b"1" # cloud devices are always clusters of at least one
|
||||
}
|
||||
|
||||
super().__init__(device_id = cluster.cluster_id, address = "",
|
||||
connection_type = ConnectionType.CloudConnection, properties = properties, parent = parent)
|
||||
super().__init__(device_id=cluster.cluster_id, address="",
|
||||
connection_type=ConnectionType.CloudConnection, properties=properties, parent=parent)
|
||||
self._api = api_client
|
||||
self._cluster = cluster
|
||||
|
||||
|
@ -104,7 +100,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
# We keep track of which printer is visible in the monitor page.
|
||||
self._active_printer = None # type: Optional[PrinterOutputModel]
|
||||
self._host_machine_type = ""
|
||||
|
||||
# Properties to populate later on with received cloud data.
|
||||
self._print_jobs = [] # type: List[UM3PrintJobOutputModel]
|
||||
|
@ -176,15 +171,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via Cloud"))
|
||||
|
||||
## Called when Cura requests an output device to receive a (G-code) file.
|
||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||
|
||||
# Show an error message if we're already sending a job.
|
||||
if self._progress.visible:
|
||||
message = Message(
|
||||
text = I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job."),
|
||||
title = I18N_CATALOG.i18nc("@info:title", "Cloud error"),
|
||||
lifetime = 10
|
||||
text=I18N_CATALOG.i18nc("@info:status",
|
||||
"Sending new jobs (temporarily) blocked, still sending the previous print job."),
|
||||
title=I18N_CATALOG.i18nc("@info:title", "Cloud error"),
|
||||
lifetime=10
|
||||
)
|
||||
message.show()
|
||||
return
|
||||
|
@ -206,9 +202,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._tool_path = mesh
|
||||
request = CloudPrintJobUploadRequest(
|
||||
job_name = file_name or mesh_format.file_extension,
|
||||
file_size = len(mesh),
|
||||
content_type = mesh_format.mime_type,
|
||||
job_name=file_name or mesh_format.file_extension,
|
||||
file_size=len(mesh),
|
||||
content_type=mesh_format.mime_type,
|
||||
)
|
||||
self._api.requestUpload(request, self._onPrintJobCreated)
|
||||
|
||||
|
@ -240,70 +236,74 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
self._updatePrintJobs(status.print_jobs)
|
||||
|
||||
## Updates the local list of printers with the list received from the cloud.
|
||||
# \param jobs: The printers received from the cloud.
|
||||
def _updatePrinters(self, printers: List[CloudClusterPrinterStatus]) -> None:
|
||||
previous = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel]
|
||||
received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinterStatus]
|
||||
# \param remote_printers: The printers received from the cloud.
|
||||
def _updatePrinters(self, remote_printers: List[CloudClusterPrinterStatus]) -> None:
|
||||
|
||||
if len(printers) > 0:
|
||||
# We need the machine type of the host (1st list entry) to allow discovery to work.
|
||||
self._host_machine_type = printers[0].machine_variant
|
||||
# Keep track of the new printers to show.
|
||||
# We create a new list instead of changing the existing one to get the correct order.
|
||||
new_printers = []
|
||||
|
||||
removed_printers, added_printers, updated_printers = findChanges(previous, received)
|
||||
# Check which printers need to be created or updated.
|
||||
for index, printer_data in enumerate(remote_printers):
|
||||
printer = next(iter(printer for printer in self._printers if printer.key == printer_data.uuid), None)
|
||||
if not printer:
|
||||
new_printers.append(printer_data.createOutputModel(CloudOutputController(self)))
|
||||
else:
|
||||
printer_data.updateOutputModel(printer)
|
||||
new_printers.append(printer)
|
||||
|
||||
# Check which printers need to be removed (de-referenced).
|
||||
remote_printers_keys = [printer_data.uuid for printer_data in remote_printers]
|
||||
removed_printers = [printer for printer in self._printers if printer.key not in remote_printers_keys]
|
||||
for removed_printer in removed_printers:
|
||||
if self._active_printer == removed_printer:
|
||||
if self._active_printer and self._active_printer.key == removed_printer.key:
|
||||
self.setActivePrinter(None)
|
||||
self._printers.remove(removed_printer)
|
||||
|
||||
for added_printer in added_printers:
|
||||
self._printers.append(added_printer.createOutputModel(CloudOutputController(self)))
|
||||
|
||||
for model, printer in updated_printers:
|
||||
printer.updateOutputModel(model)
|
||||
|
||||
# Always have an active printer
|
||||
if self._printers and not self._active_printer:
|
||||
self._printers = new_printers
|
||||
if self._printers and not self.activePrinter:
|
||||
self.setActivePrinter(self._printers[0])
|
||||
|
||||
if added_printers or removed_printers:
|
||||
self.printersChanged.emit()
|
||||
self.printersChanged.emit()
|
||||
|
||||
## Updates the local list of print jobs with the list received from the cloud.
|
||||
# \param jobs: The print jobs received from the cloud.
|
||||
def _updatePrintJobs(self, jobs: List[CloudClusterPrintJobStatus]) -> None:
|
||||
received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJobStatus]
|
||||
previous = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel]
|
||||
# \param remote_jobs: The print jobs received from the cloud.
|
||||
def _updatePrintJobs(self, remote_jobs: List[CloudClusterPrintJobStatus]) -> None:
|
||||
|
||||
removed_jobs, added_jobs, updated_jobs = findChanges(previous, received)
|
||||
# Keep track of the new print jobs to show.
|
||||
# We create a new list instead of changing the existing one to get the correct order.
|
||||
new_print_jobs = []
|
||||
|
||||
# Check which print jobs need to be created or updated.
|
||||
for index, print_job_data in enumerate(remote_jobs):
|
||||
print_job = next(
|
||||
iter(print_job for print_job in self._print_jobs if print_job.key == print_job_data.uuid), None)
|
||||
if not print_job:
|
||||
new_print_jobs.append(self._createPrintJobModel(print_job_data))
|
||||
else:
|
||||
print_job_data.updateOutputModel(print_job)
|
||||
if print_job_data.printer_uuid:
|
||||
self._updateAssignedPrinter(print_job, print_job_data.printer_uuid)
|
||||
new_print_jobs.append(print_job)
|
||||
|
||||
# Check which print job need to be removed (de-referenced).
|
||||
remote_job_keys = [print_job_data.uuid for print_job_data in remote_jobs]
|
||||
removed_jobs = [print_job for print_job in self._print_jobs if print_job.key not in remote_job_keys]
|
||||
for removed_job in removed_jobs:
|
||||
if removed_job.assignedPrinter:
|
||||
removed_job.assignedPrinter.updateActivePrintJob(None)
|
||||
removed_job.stateChanged.disconnect(self._onPrintJobStateChanged)
|
||||
self._print_jobs.remove(removed_job)
|
||||
|
||||
for added_job in added_jobs:
|
||||
self._addPrintJob(added_job)
|
||||
self._print_jobs = new_print_jobs
|
||||
self.printJobsChanged.emit()
|
||||
|
||||
for model, job in updated_jobs:
|
||||
job.updateOutputModel(model)
|
||||
if job.printer_uuid:
|
||||
self._updateAssignedPrinter(model, job.printer_uuid)
|
||||
|
||||
# We only have to update when jobs are added or removed
|
||||
# updated jobs push their changes via their output model
|
||||
if added_jobs or removed_jobs:
|
||||
self.printJobsChanged.emit()
|
||||
|
||||
## Registers a new print job received via the cloud API.
|
||||
# \param job: The print job received.
|
||||
def _addPrintJob(self, job: CloudClusterPrintJobStatus) -> None:
|
||||
model = job.createOutputModel(CloudOutputController(self))
|
||||
## Create a new print job model based on the remote status of the job.
|
||||
# \param remote_job: The remote print job data.
|
||||
def _createPrintJobModel(self, remote_job: CloudClusterPrintJobStatus) -> UM3PrintJobOutputModel:
|
||||
model = remote_job.createOutputModel(CloudOutputController(self))
|
||||
model.stateChanged.connect(self._onPrintJobStateChanged)
|
||||
if job.printer_uuid:
|
||||
self._updateAssignedPrinter(model, job.printer_uuid)
|
||||
self._print_jobs.append(model)
|
||||
if remote_job.printer_uuid:
|
||||
self._updateAssignedPrinter(model, remote_job.printer_uuid)
|
||||
return model
|
||||
|
||||
## Handles the event of a change in a print job state
|
||||
def _onPrintJobStateChanged(self) -> None:
|
||||
|
@ -313,14 +313,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
if job.state == "wait_cleanup" and job.key not in self._finished_jobs and job.owner == user_name:
|
||||
self._finished_jobs.add(job.key)
|
||||
Message(
|
||||
title = I18N_CATALOG.i18nc("@info:status", "Print finished"),
|
||||
text = (I18N_CATALOG.i18nc("@info:status", "Printer '{printer_name}' has finished printing '{job_name}'.").format(
|
||||
printer_name = job.assignedPrinter.name,
|
||||
job_name = job.name
|
||||
title=I18N_CATALOG.i18nc("@info:status", "Print finished"),
|
||||
text=(I18N_CATALOG.i18nc("@info:status",
|
||||
"Printer '{printer_name}' has finished printing '{job_name}'.").format(
|
||||
printer_name=job.assignedPrinter.name,
|
||||
job_name=job.name
|
||||
) if job.assignedPrinter else
|
||||
I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.").format(
|
||||
job_name = job.name
|
||||
)),
|
||||
I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.").format(
|
||||
job_name=job.name
|
||||
)),
|
||||
).show()
|
||||
|
||||
## Updates the printer assignment for the given print job model.
|
||||
|
@ -328,9 +329,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
printer = next((p for p in self._printers if printer_uuid == p.key), None)
|
||||
if not printer:
|
||||
Logger.log("w", "Missing printer %s for job %s in %s", model.assignedPrinter, model.key,
|
||||
[p.key for p in self._printers])
|
||||
[p.key for p in self._printers])
|
||||
return
|
||||
|
||||
printer.updateActivePrintJob(model)
|
||||
model.updateAssignedPrinter(printer)
|
||||
|
||||
|
@ -340,7 +340,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
self._progress.show()
|
||||
self._uploaded_print_job = job_response
|
||||
tool_path = cast(bytes, self._tool_path)
|
||||
self._api.uploadToolPath(job_response, tool_path, self._onPrintJobUploaded, self._progress.update, self._onUploadError)
|
||||
self._api.uploadToolPath(job_response, tool_path, self._onPrintJobUploaded, self._progress.update,
|
||||
self._onUploadError)
|
||||
|
||||
## Requests the print to be sent to the printer when we finished uploading the mesh.
|
||||
def _onPrintJobUploaded(self) -> None:
|
||||
|
@ -354,9 +355,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
self._progress.hide()
|
||||
self._uploaded_print_job = None
|
||||
Message(
|
||||
text = message or I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer."),
|
||||
title = I18N_CATALOG.i18nc("@info:title", "Cloud error"),
|
||||
lifetime = 10
|
||||
text=message or I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer."),
|
||||
title=I18N_CATALOG.i18nc("@info:title", "Cloud error"),
|
||||
lifetime=10
|
||||
).show()
|
||||
self.writeError.emit()
|
||||
|
||||
|
@ -366,22 +367,24 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id)
|
||||
self._progress.hide()
|
||||
Message(
|
||||
text = I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
||||
title = I18N_CATALOG.i18nc("@info:title", "Data Sent"),
|
||||
lifetime = 5
|
||||
text=I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
||||
title=I18N_CATALOG.i18nc("@info:title", "Data Sent"),
|
||||
lifetime=5
|
||||
).show()
|
||||
self.writeFinished.emit()
|
||||
|
||||
## Gets the printer type of the cluster host. Falls back to the printer type in the device properties.
|
||||
@pyqtProperty(str, notify=_clusterPrintersChanged)
|
||||
def printerType(self) -> str:
|
||||
if self._printers and self._host_machine_type in self._host_machine_variant_to_machine_type_map:
|
||||
return self._host_machine_variant_to_machine_type_map[self._host_machine_type]
|
||||
return super().printerType
|
||||
## Whether the printer that this output device represents supports print job actions via the cloud.
|
||||
@pyqtProperty(bool, notify=_clusterPrintersChanged)
|
||||
def supportsPrintJobActions(self) -> bool:
|
||||
if not self._printers:
|
||||
return False
|
||||
version_number = self.printers[0].firmwareVersion.split(".")
|
||||
firmware_version = Version([version_number[0], version_number[1], version_number[2]])
|
||||
return firmware_version >= self.PRINT_JOB_ACTIONS_MIN_VERSION
|
||||
|
||||
## Gets the number of printers in the cluster.
|
||||
# We use a minimum of 1 because cloud devices are always a cluster and printer discovery needs it.
|
||||
@pyqtProperty(int, notify = _clusterPrintersChanged)
|
||||
@pyqtProperty(int, notify=_clusterPrintersChanged)
|
||||
def clusterSize(self) -> int:
|
||||
return max(1, len(self._printers))
|
||||
|
||||
|
@ -391,7 +394,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
return self._printers
|
||||
|
||||
## Get the active printer in the UI (monitor page).
|
||||
@pyqtProperty(QObject, notify = activePrinterChanged)
|
||||
@pyqtProperty(QObject, notify=activePrinterChanged)
|
||||
def activePrinter(self) -> Optional[PrinterOutputModel]:
|
||||
return self._active_printer
|
||||
|
||||
|
@ -403,38 +406,67 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
self.activePrinterChanged.emit()
|
||||
|
||||
## Get remote print jobs.
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def printJobs(self) -> List[UM3PrintJobOutputModel]:
|
||||
return self._print_jobs
|
||||
|
||||
## Get remote print jobs that are still in the print queue.
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]:
|
||||
return [print_job for print_job in self._print_jobs
|
||||
if print_job.state == "queued" or print_job.state == "error"]
|
||||
|
||||
## Get remote print jobs that are assigned to a printer.
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def activePrintJobs(self) -> List[UM3PrintJobOutputModel]:
|
||||
return [print_job for print_job in self._print_jobs if
|
||||
print_job.assignedPrinter is not None and print_job.state != "queued"]
|
||||
|
||||
@pyqtSlot(int, result = str)
|
||||
## Set the remote print job state.
|
||||
def setJobState(self, print_job_uuid: str, state: str) -> None:
|
||||
self._api.doPrintJobAction(self._cluster.cluster_id, print_job_uuid, state)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def sendJobToTop(self, print_job_uuid: str) -> None:
|
||||
self._api.doPrintJobAction(self._cluster.cluster_id, print_job_uuid, "move",
|
||||
{"list": "queued", "to_position": 0})
|
||||
|
||||
@pyqtSlot(str)
|
||||
def deleteJobFromQueue(self, print_job_uuid: str) -> None:
|
||||
self._api.doPrintJobAction(self._cluster.cluster_id, print_job_uuid, "remove")
|
||||
|
||||
@pyqtSlot(str)
|
||||
def forceSendJob(self, print_job_uuid: str) -> None:
|
||||
self._api.doPrintJobAction(self._cluster.cluster_id, print_job_uuid, "force")
|
||||
|
||||
@pyqtSlot(int, result=str)
|
||||
def formatDuration(self, seconds: int) -> str:
|
||||
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
|
||||
|
||||
@pyqtSlot(int, result = str)
|
||||
@pyqtSlot(int, result=str)
|
||||
def getTimeCompleted(self, time_remaining: int) -> str:
|
||||
return formatTimeCompleted(time_remaining)
|
||||
|
||||
@pyqtSlot(int, result = str)
|
||||
@pyqtSlot(int, result=str)
|
||||
def getDateCompleted(self, time_remaining: int) -> str:
|
||||
return formatDateCompleted(time_remaining)
|
||||
|
||||
@pyqtProperty(bool, notify=printJobsChanged)
|
||||
def receivedPrintJobs(self) -> bool:
|
||||
return bool(self._print_jobs)
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrintJobControlPanel(self) -> None:
|
||||
QDesktopServices.openUrl(QUrl("https://mycloud.ultimaker.com"))
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrinterControlPanel(self) -> None:
|
||||
QDesktopServices.openUrl(QUrl("https://mycloud.ultimaker.com"))
|
||||
|
||||
## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud.
|
||||
# TODO: We fake the methods here to not break the monitor page.
|
||||
|
||||
@pyqtProperty(QUrl, notify = _clusterPrintersChanged)
|
||||
@pyqtProperty(QUrl, notify=_clusterPrintersChanged)
|
||||
def activeCameraUrl(self) -> "QUrl":
|
||||
return QUrl()
|
||||
|
||||
|
@ -442,30 +474,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
def setActiveCameraUrl(self, camera_url: "QUrl") -> None:
|
||||
pass
|
||||
|
||||
@pyqtProperty(bool, notify = printJobsChanged)
|
||||
def receivedPrintJobs(self) -> bool:
|
||||
return bool(self._print_jobs)
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrintJobControlPanel(self) -> None:
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrinterControlPanel(self) -> None:
|
||||
pass
|
||||
|
||||
@pyqtSlot(str)
|
||||
def sendJobToTop(self, print_job_uuid: str) -> None:
|
||||
pass
|
||||
|
||||
@pyqtSlot(str)
|
||||
def deleteJobFromQueue(self, print_job_uuid: str) -> None:
|
||||
pass
|
||||
|
||||
@pyqtSlot(str)
|
||||
def forceSendJob(self, print_job_uuid: str) -> None:
|
||||
pass
|
||||
|
||||
@pyqtProperty("QVariantList", notify = _clusterPrintersChanged)
|
||||
@pyqtProperty("QVariantList", notify=_clusterPrintersChanged)
|
||||
def connectedPrintersTypeCount(self) -> List[Dict[str, str]]:
|
||||
return []
|
||||
|
|
|
@ -96,7 +96,7 @@ class CloudOutputDeviceManager:
|
|||
device = CloudOutputDevice(self._api, cluster)
|
||||
self._remote_clusters[cluster.cluster_id] = device
|
||||
self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(
|
||||
cluster.cluster_id,
|
||||
device.key,
|
||||
device.key,
|
||||
cluster.friendly_name,
|
||||
self._createMachineFromDiscoveredPrinter,
|
||||
|
@ -109,7 +109,7 @@ class CloudOutputDeviceManager:
|
|||
for device, cluster in updates:
|
||||
device.clusterData = cluster
|
||||
self._application.getDiscoveredPrintersModel().updateDiscoveredPrinter(
|
||||
cluster.cluster_id,
|
||||
device.key,
|
||||
cluster.friendly_name,
|
||||
device.printerType,
|
||||
)
|
||||
|
|
|
@ -91,7 +91,6 @@ class CloudClusterPrintJobStatus(BaseCloudModel):
|
|||
def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel:
|
||||
model = UM3PrintJobOutputModel(controller, self.uuid, self.name)
|
||||
self.updateOutputModel(model)
|
||||
|
||||
return model
|
||||
|
||||
## Creates a new configuration model
|
||||
|
|
|
@ -15,9 +15,12 @@ class CloudClusterResponse(BaseCloudModel):
|
|||
# \param is_online: Whether this cluster is currently connected to the cloud.
|
||||
# \param status: The status of the cluster authentication (active or inactive).
|
||||
# \param host_version: The firmware version of the cluster host. This is where the Stardust client is running on.
|
||||
# \param host_internal_ip: The internal IP address of the host printer.
|
||||
# \param friendly_name: The human readable name of the host printer.
|
||||
# \param printer_type: The machine type of the host printer.
|
||||
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
||||
host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
||||
friendly_name: Optional[str] = None, **kwargs) -> None:
|
||||
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", **kwargs) -> None:
|
||||
self.cluster_id = cluster_id
|
||||
self.host_guid = host_guid
|
||||
self.host_name = host_name
|
||||
|
@ -26,6 +29,7 @@ class CloudClusterResponse(BaseCloudModel):
|
|||
self.host_version = host_version
|
||||
self.host_internal_ip = host_internal_ip
|
||||
self.friendly_name = friendly_name
|
||||
self.printer_type = printer_type
|
||||
super().__init__(**kwargs)
|
||||
|
||||
# Validates the model, raising an exception if the model is invalid.
|
||||
|
|
|
@ -106,8 +106,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._active_camera_url = QUrl() # type: QUrl
|
||||
|
||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||
self.writeStarted.emit(self)
|
||||
|
||||
self.sendMaterialProfiles()
|
||||
|
@ -140,6 +140,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
if self._printer_selection_dialog is not None:
|
||||
self._printer_selection_dialog.show()
|
||||
|
||||
## Whether the printer that this output device represents supports print job actions via the local network.
|
||||
@pyqtProperty(bool, constant=True)
|
||||
def supportsPrintJobActions(self) -> bool:
|
||||
return True
|
||||
|
||||
@pyqtProperty(int, constant=True)
|
||||
def clusterSize(self) -> int:
|
||||
return self._cluster_size
|
||||
|
@ -385,6 +390,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
data = "{\"force\": true}"
|
||||
self.put("print_jobs/{uuid}".format(uuid=print_job_uuid), data, on_finished=None)
|
||||
|
||||
# Set the remote print job state.
|
||||
def setJobState(self, print_job_uuid: str, state: str) -> None:
|
||||
# We rewrite 'resume' to 'print' here because we are using the old print job action endpoints.
|
||||
action = "print" if state == "resume" else state
|
||||
data = "{\"action\": \"%s\"}" % action
|
||||
self.put("print_jobs/%s/action" % print_job_uuid, data, on_finished=None)
|
||||
|
||||
def _printJobStateChanged(self) -> None:
|
||||
username = self._getUserName()
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ MYPY = False
|
|||
if MYPY:
|
||||
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
|
||||
|
||||
|
||||
class ClusterUM3PrinterOutputController(PrinterOutputController):
|
||||
def __init__(self, output_device):
|
||||
super().__init__(output_device)
|
||||
|
@ -15,6 +16,5 @@ class ClusterUM3PrinterOutputController(PrinterOutputController):
|
|||
self.can_control_manually = False
|
||||
self.can_send_raw_gcode = False
|
||||
|
||||
def setJobState(self, job: "PrintJobOutputModel", state: str):
|
||||
data = "{\"action\": \"%s\"}" % state
|
||||
self._output_device.put("print_jobs/%s/action" % job.key, data, on_finished=None)
|
||||
def setJobState(self, job: "PrintJobOutputModel", state: str) -> None:
|
||||
self._output_device.setJobState(job.key, state)
|
||||
|
|
|
@ -3,11 +3,16 @@
|
|||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
|
||||
|
||||
BLOCKING_CHANGE_TYPES = [
|
||||
"material_insert", "buildplate_change"
|
||||
]
|
||||
|
||||
class ConfigurationChangeModel(QObject):
|
||||
def __init__(self, type_of_change: str, index: int, target_name: str, origin_name: str) -> None:
|
||||
super().__init__()
|
||||
self._type_of_change = type_of_change
|
||||
# enum = ["material", "print_core_change"]
|
||||
self._can_override = self._type_of_change not in BLOCKING_CHANGE_TYPES
|
||||
self._index = index
|
||||
self._target_name = target_name
|
||||
self._origin_name = origin_name
|
||||
|
@ -27,3 +32,7 @@ class ConfigurationChangeModel(QObject):
|
|||
@pyqtProperty(str, constant = True)
|
||||
def originName(self) -> str:
|
||||
return self._origin_name
|
||||
|
||||
@pyqtProperty(bool, constant = True)
|
||||
def canOverride(self) -> bool:
|
||||
return self._can_override
|
|
@ -178,7 +178,8 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
# NotImplementedError. We can simply ignore these.
|
||||
pass
|
||||
|
||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||
if not self.activePrinter:
|
||||
# No active printer. Unable to write
|
||||
return
|
||||
|
|
|
@ -27,7 +27,7 @@ from UM.Version import Version
|
|||
|
||||
from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice
|
||||
from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager
|
||||
from .Cloud.CloudOutputDevice import CloudOutputDevice # typing
|
||||
from .Cloud.CloudOutputDevice import CloudOutputDevice # typing
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from PyQt5.QtNetwork import QNetworkReply
|
||||
|
@ -168,7 +168,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
if address:
|
||||
self.addManualDevice(address)
|
||||
self.resetLastManualDevice()
|
||||
|
||||
|
||||
# TODO: CHANGE TO HOSTNAME
|
||||
def refreshConnections(self):
|
||||
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not active_machine:
|
||||
|
@ -235,10 +236,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
self._application.callLater(manual_printer_request.callback, False, address)
|
||||
|
||||
def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None:
|
||||
if address in self._manual_instances:
|
||||
Logger.log("i", "Manual printer with address [%s] has already been added, do nothing", address)
|
||||
return
|
||||
|
||||
self._manual_instances[address] = ManualPrinterRequest(address, callback = callback)
|
||||
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys()))
|
||||
|
||||
|
|
|
@ -72,9 +72,9 @@ class TestCloudOutputDevice(TestCase):
|
|||
|
||||
controller_fields = {
|
||||
"_output_device": self.device,
|
||||
"can_abort": False,
|
||||
"can_abort": True,
|
||||
"can_control_manually": False,
|
||||
"can_pause": False,
|
||||
"can_pause": True,
|
||||
"can_pre_heat_bed": False,
|
||||
"can_pre_heat_hotends": False,
|
||||
"can_send_raw_gcode": False,
|
||||
|
|
|
@ -6,6 +6,7 @@ import os
|
|||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.Mesh.MeshWriter import MeshWriter #To get the g-code output.
|
||||
from UM.Message import Message #Show an error when already printing.
|
||||
from UM.PluginRegistry import PluginRegistry #To get the g-code output.
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
|
||||
|
@ -23,11 +24,15 @@ from queue import Queue
|
|||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
from time import time
|
||||
from typing import Union, Optional, List, cast
|
||||
from typing import Union, Optional, List, cast, TYPE_CHECKING
|
||||
|
||||
import re
|
||||
import functools # Used for reduce
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.FileHandler.FileHandler import FileHandler
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -112,16 +117,20 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
## Request the current scene to be sent to a USB-connected printer.
|
||||
#
|
||||
# \param nodes A collection of scene nodes to send. This is ignored.
|
||||
# \param file_name \type{string} A suggestion for a file name to write.
|
||||
# \param file_name A suggestion for a file name to write.
|
||||
# \param filter_by_machine Whether to filter MIME types by machine. This
|
||||
# is ignored.
|
||||
# \param kwargs Keyword arguments.
|
||||
def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs):
|
||||
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||
if self._is_printing:
|
||||
message = Message(text = catalog.i18nc("@message", "A print is still in progress. Cura cannot start another print via USB until the previous print has completed."), title = catalog.i18nc("@message", "Print in Progress"))
|
||||
message.show()
|
||||
return # Already printing
|
||||
self.writeStarted.emit(self)
|
||||
# cancel any ongoing preheat timer before starting a print
|
||||
self._printers[0].getController().stopPreheatTimers()
|
||||
controller = cast(GenericOutputController, self._printers[0].getController())
|
||||
controller.stopPreheatTimers()
|
||||
|
||||
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
|
||||
|
||||
|
@ -181,7 +190,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
try:
|
||||
self._serial = Serial(str(self._serial_port), self._baud_rate, timeout=self._timeout, writeTimeout=self._timeout)
|
||||
except SerialException:
|
||||
Logger.log("w", "An exception occured while trying to create serial connection")
|
||||
Logger.log("w", "An exception occurred while trying to create serial connection")
|
||||
return
|
||||
CuraApplication.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
import threading
|
||||
import time
|
||||
import serial.tools.list_ports
|
||||
from os import environ
|
||||
from re import search
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
|
||||
|
@ -112,6 +114,27 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
port = (port.device, port.description, port.hwid)
|
||||
if only_list_usb and not port[2].startswith("USB"):
|
||||
continue
|
||||
|
||||
# To prevent cura from messing with serial ports of other devices,
|
||||
# filter by regular expressions passed in as environment variables.
|
||||
# Get possible patterns with python3 -m serial.tools.list_ports -v
|
||||
|
||||
# set CURA_DEVICENAMES=USB[1-9] -> e.g. not matching /dev/ttyUSB0
|
||||
pattern = environ.get('CURA_DEVICENAMES')
|
||||
if pattern and not search(pattern, port[0]):
|
||||
continue
|
||||
|
||||
# set CURA_DEVICETYPES=CP2102 -> match a type of serial converter
|
||||
pattern = environ.get('CURA_DEVICETYPES')
|
||||
if pattern and not search(pattern, port[1]):
|
||||
continue
|
||||
|
||||
# set CURA_DEVICEINFOS=LOCATION=2-1.4 -> match a physical port
|
||||
# set CURA_DEVICEINFOS=VID:PID=10C4:EA60 -> match a vendor:product
|
||||
pattern = environ.get('CURA_DEVICEINFOS')
|
||||
if pattern and not search(pattern, port[2]):
|
||||
continue
|
||||
|
||||
base_list += [port[0]]
|
||||
|
||||
return list(base_list)
|
||||
|
|
|
@ -52,7 +52,7 @@ class VersionUpgrade40to41(VersionUpgrade):
|
|||
parser["metadata"]["setting_version"] = "7"
|
||||
|
||||
# Limit Maximum Deviation instead of Maximum Resolution. This should have approximately the same effect as before the algorithm change, only more consistent.
|
||||
if "meshfix_maximum_resolution" in parser["values"]:
|
||||
if "values" in parser and "meshfix_maximum_resolution" in parser["values"]:
|
||||
resolution = parser["values"]["meshfix_maximum_resolution"]
|
||||
if resolution.startswith("="):
|
||||
resolution = resolution[1:]
|
||||
|
|
|
@ -14,7 +14,7 @@ def getMetaData() -> Dict[str, Any]:
|
|||
return {
|
||||
"version_upgrade": {
|
||||
# From To Upgrade function
|
||||
("preferences", 6000006): ("preferences", 6000007, upgrade.upgradePreferences),
|
||||
("preferences", 6000006): ("preferences", 6000007, upgrade.upgradePreferences),
|
||||
("machine_stack", 4000006): ("machine_stack", 4000007, upgrade.upgradeStack),
|
||||
("extruder_train", 4000006): ("extruder_train", 4000007, upgrade.upgradeStack),
|
||||
("definition_changes", 4000006): ("definition_changes", 4000007, upgrade.upgradeInstanceContainer),
|
||||
|
|
|
@ -0,0 +1,337 @@
|
|||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser
|
||||
import io
|
||||
import os.path # To get the file ID.
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from UM.VersionUpgrade import VersionUpgrade
|
||||
|
||||
_renamed_settings = {
|
||||
"support_minimal_diameter": "support_tower_maximum_supported_diameter"
|
||||
} # type: Dict[str, str]
|
||||
_removed_settings = ["prime_tower_circular", "max_feedrate_z_override"] # type: List[str]
|
||||
_renamed_profiles = {
|
||||
# Include CreawsomeMod profiles here as well for the people who installed that.
|
||||
# Definitions.
|
||||
"creawsome_base": "creality_base",
|
||||
"creawsome_cr10": "creality_cr10",
|
||||
"creawsome_cr10mini": "creality_cr10mini",
|
||||
"creawsome_cr10s": "creality_cr10s",
|
||||
"creawsome_cr10s4": "creality_cr10s4",
|
||||
"creawsome_cr10s5": "creality_cr10s5",
|
||||
"creawsome_cr10spro": "creality_cr10spro",
|
||||
"creawsome_cr20": "creality_cr20",
|
||||
"creawsome_cr20pro": "creality_cr20pro",
|
||||
"creawsome_ender2": "creality_ender2",
|
||||
"creawsome_ender3": "creality_ender3",
|
||||
"creawsome_ender4": "creality_ender4",
|
||||
"creawsome_ender5": "creality_ender5",
|
||||
|
||||
# Extruder definitions.
|
||||
"creawsome_base_extruder_0": "creality_base_extruder_0",
|
||||
|
||||
# Variants.
|
||||
"creawsome_base_0.2": "creality_base_0.2",
|
||||
"creawsome_base_0.3": "creality_base_0.3",
|
||||
"creawsome_base_0.4": "creality_base_0.4",
|
||||
"creawsome_base_0.5": "creality_base_0.5",
|
||||
"creawsome_base_0.6": "creality_base_0.6",
|
||||
"creawsome_base_0.8": "creality_base_0.8",
|
||||
"creawsome_base_1.0": "creality_base_1.0",
|
||||
"creawsome_cr10_0.2": "creality_cr10_0.2",
|
||||
"creawsome_cr10_0.3": "creality_cr10_0.3",
|
||||
"creawsome_cr10_0.4": "creality_cr10_0.4",
|
||||
"creawsome_cr10_0.5": "creality_cr10_0.5",
|
||||
"creawsome_cr10_0.6": "creality_cr10_0.6",
|
||||
"creawsome_cr10_0.8": "creality_cr10_0.8",
|
||||
"creawsome_cr10_1.0": "creality_cr10_1.0",
|
||||
"creawsome_cr10mini_0.2": "creality_cr10mini_0.2",
|
||||
"creawsome_cr10mini_0.3": "creality_cr10mini_0.3",
|
||||
"creawsome_cr10mini_0.4": "creality_cr10mini_0.4",
|
||||
"creawsome_cr10mini_0.5": "creality_cr10mini_0.5",
|
||||
"creawsome_cr10mini_0.6": "creality_cr10mini_0.6",
|
||||
"creawsome_cr10mini_0.8": "creality_cr10mini_0.8",
|
||||
"creawsome_cr10mini_1.0": "creality_cr10mini_1.0",
|
||||
"creawsome_cr10s4_0.2": "creality_cr10s4_0.2",
|
||||
"creawsome_cr10s4_0.3": "creality_cr10s4_0.3",
|
||||
"creawsome_cr10s4_0.4": "creality_cr10s4_0.4",
|
||||
"creawsome_cr10s4_0.5": "creality_cr10s4_0.5",
|
||||
"creawsome_cr10s4_0.6": "creality_cr10s4_0.6",
|
||||
"creawsome_cr10s4_0.8": "creality_cr10s4_0.8",
|
||||
"creawsome_cr10s4_1.0": "creality_cr10s4_1.0",
|
||||
"creawsome_cr10s5_0.2": "creality_cr10s5_0.2",
|
||||
"creawsome_cr10s5_0.3": "creality_cr10s5_0.3",
|
||||
"creawsome_cr10s5_0.4": "creality_cr10s5_0.4",
|
||||
"creawsome_cr10s5_0.5": "creality_cr10s5_0.5",
|
||||
"creawsome_cr10s5_0.6": "creality_cr10s5_0.6",
|
||||
"creawsome_cr10s5_0.8": "creality_cr10s5_0.8",
|
||||
"creawsome_cr10s5_1.0": "creality_cr10s5_1.0",
|
||||
"creawsome_cr10s_0.2": "creality_cr10s_0.2",
|
||||
"creawsome_cr10s_0.3": "creality_cr10s_0.3",
|
||||
"creawsome_cr10s_0.4": "creality_cr10s_0.4",
|
||||
"creawsome_cr10s_0.5": "creality_cr10s_0.5",
|
||||
"creawsome_cr10s_0.6": "creality_cr10s_0.6",
|
||||
"creawsome_cr10s_0.8": "creality_cr10s_0.8",
|
||||
"creawsome_cr10s_1.0": "creality_cr10s_1.0",
|
||||
"creawsome_cr10spro_0.2": "creality_cr10spro_0.2",
|
||||
"creawsome_cr10spro_0.3": "creality_cr10spro_0.3",
|
||||
"creawsome_cr10spro_0.4": "creality_cr10spro_0.4",
|
||||
"creawsome_cr10spro_0.5": "creality_cr10spro_0.5",
|
||||
"creawsome_cr10spro_0.6": "creality_cr10spro_0.6",
|
||||
"creawsome_cr10spro_0.8": "creality_cr10spro_0.8",
|
||||
"creawsome_cr10spro_1.0": "creality_cr10spro_1.0",
|
||||
"creawsome_cr20_0.2": "creality_cr20_0.2",
|
||||
"creawsome_cr20_0.3": "creality_cr20_0.3",
|
||||
"creawsome_cr20_0.4": "creality_cr20_0.4",
|
||||
"creawsome_cr20_0.5": "creality_cr20_0.5",
|
||||
"creawsome_cr20_0.6": "creality_cr20_0.6",
|
||||
"creawsome_cr20_0.8": "creality_cr20_0.8",
|
||||
"creawsome_cr20_1.0": "creality_cr20_1.0",
|
||||
"creawsome_cr20pro_0.2": "creality_cr20pro_0.2",
|
||||
"creawsome_cr20pro_0.3": "creality_cr20pro_0.3",
|
||||
"creawsome_cr20pro_0.4": "creality_cr20pro_0.4",
|
||||
"creawsome_cr20pro_0.5": "creality_cr20pro_0.5",
|
||||
"creawsome_cr20pro_0.6": "creality_cr20pro_0.6",
|
||||
"creawsome_cr20pro_0.8": "creality_cr20pro_0.8",
|
||||
"creawsome_cr20pro_1.0": "creality_cr20pro_1.0",
|
||||
"creawsome_ender2_0.2": "creality_ender2_0.2",
|
||||
"creawsome_ender2_0.3": "creality_ender2_0.3",
|
||||
"creawsome_ender2_0.4": "creality_ender2_0.4",
|
||||
"creawsome_ender2_0.5": "creality_ender2_0.5",
|
||||
"creawsome_ender2_0.6": "creality_ender2_0.6",
|
||||
"creawsome_ender2_0.8": "creality_ender2_0.8",
|
||||
"creawsome_ender2_1.0": "creality_ender2_1.0",
|
||||
"creawsome_ender3_0.2": "creality_ender3_0.2",
|
||||
"creawsome_ender3_0.3": "creality_ender3_0.3",
|
||||
"creawsome_ender3_0.4": "creality_ender3_0.4",
|
||||
"creawsome_ender3_0.5": "creality_ender3_0.5",
|
||||
"creawsome_ender3_0.6": "creality_ender3_0.6",
|
||||
"creawsome_ender3_0.8": "creality_ender3_0.8",
|
||||
"creawsome_ender3_1.0": "creality_ender3_1.0",
|
||||
"creawsome_ender4_0.2": "creality_ender4_0.2",
|
||||
"creawsome_ender4_0.3": "creality_ender4_0.3",
|
||||
"creawsome_ender4_0.4": "creality_ender4_0.4",
|
||||
"creawsome_ender4_0.5": "creality_ender4_0.5",
|
||||
"creawsome_ender4_0.6": "creality_ender4_0.6",
|
||||
"creawsome_ender4_0.8": "creality_ender4_0.8",
|
||||
"creawsome_ender4_1.0": "creality_ender4_1.0",
|
||||
"creawsome_ender5_0.2": "creality_ender5_0.2",
|
||||
"creawsome_ender5_0.3": "creality_ender5_0.3",
|
||||
"creawsome_ender5_0.4": "creality_ender5_0.4",
|
||||
"creawsome_ender5_0.5": "creality_ender5_0.5",
|
||||
"creawsome_ender5_0.6": "creality_ender5_0.6",
|
||||
"creawsome_ender5_0.8": "creality_ender5_0.8",
|
||||
"creawsome_ender5_1.0": "creality_ender5_1.0",
|
||||
|
||||
# Upgrade for people who had the original Creality profiles from 4.1 and earlier.
|
||||
"creality_cr10_extruder_0": "creality_base_extruder_0",
|
||||
"creality_cr10s4_extruder_0": "creality_base_extruder_0",
|
||||
"creality_cr10s5_extruder_0": "creality_base_extruder_0",
|
||||
"creality_ender3_extruder_0": "creality_base_extruder_0"
|
||||
}
|
||||
|
||||
# For legacy Creality printers, select the correct quality profile depending on the material.
|
||||
_creality_quality_per_material = {
|
||||
# Since legacy Creality printers didn't have different variants, we always pick the 0.4mm variant.
|
||||
"generic_abs_175": {
|
||||
"high": "base_0.4_ABS_super",
|
||||
"normal": "base_0.4_ABS_super",
|
||||
"fast": "base_0.4_ABS_super",
|
||||
"draft": "base_0.4_ABS_standard",
|
||||
"extra_fast": "base_0.4_ABS_low",
|
||||
"coarse": "base_0.4_ABS_low",
|
||||
"extra_coarse": "base_0.4_ABS_low"
|
||||
},
|
||||
"generic_petg_175": {
|
||||
"high": "base_0.4_PETG_super",
|
||||
"normal": "base_0.4_PETG_super",
|
||||
"fast": "base_0.4_PETG_super",
|
||||
"draft": "base_0.4_PETG_standard",
|
||||
"extra_fast": "base_0.4_PETG_low",
|
||||
"coarse": "base_0.4_PETG_low",
|
||||
"extra_coarse": "base_0.4_PETG_low"
|
||||
},
|
||||
"generic_pla_175": {
|
||||
"high": "base_0.4_PLA_super",
|
||||
"normal": "base_0.4_PLA_super",
|
||||
"fast": "base_0.4_PLA_super",
|
||||
"draft": "base_0.4_PLA_standard",
|
||||
"extra_fast": "base_0.4_PLA_low",
|
||||
"coarse": "base_0.4_PLA_low",
|
||||
"extra_coarse": "base_0.4_PLA_low"
|
||||
},
|
||||
"generic_tpu_175": {
|
||||
"high": "base_0.4_TPU_super",
|
||||
"normal": "base_0.4_TPU_super",
|
||||
"fast": "base_0.4_TPU_super",
|
||||
"draft": "base_0.4_TPU_standard",
|
||||
"extra_fast": "base_0.4_TPU_standard",
|
||||
"coarse": "base_0.4_TPU_standard",
|
||||
"extra_coarse": "base_0.4_TPU_standard"
|
||||
},
|
||||
"empty_material": { # For the global stack.
|
||||
"high": "base_global_super",
|
||||
"normal": "base_global_super",
|
||||
"fast": "base_global_super",
|
||||
"draft": "base_global_standard",
|
||||
"extra_fast": "base_global_low",
|
||||
"coarse": "base_global_low",
|
||||
"extra_coarse": "base_global_low"
|
||||
}
|
||||
}
|
||||
|
||||
# Default variant to select for legacy Creality printers, now that we have variants.
|
||||
_default_variants = {
|
||||
"creality_cr10_extruder_0": "creality_cr10_0.4",
|
||||
"creality_cr10s4_extruder_0": "creality_cr10s4_0.4",
|
||||
"creality_cr10s5_extruder_0": "creality_cr10s5_0.4",
|
||||
"creality_ender3_extruder_0": "creality_ender3_0.4"
|
||||
}
|
||||
|
||||
# Whether the quality changes profile belongs to one of the upgraded printers can only be recognised by how they start.
|
||||
# If they are, they must use the creality base definition so that they still belong to those printers.
|
||||
_quality_changes_to_creality_base = {
|
||||
"creality_cr10_extruder_0",
|
||||
"creality_cr10s4_extruder_0",
|
||||
"creality_cr10s5_extruder_0",
|
||||
"creality_ender3_extruder_0"
|
||||
"creality_cr10",
|
||||
"creality_cr10s4",
|
||||
"creality_cr10s5",
|
||||
"creality_ender3",
|
||||
}
|
||||
_creality_limited_quality_type = {
|
||||
"high": "super",
|
||||
"normal": "super",
|
||||
"fast": "super",
|
||||
"draft": "draft",
|
||||
"extra_fast": "draft",
|
||||
"coarse": "draft",
|
||||
"extra_coarse": "draft"
|
||||
}
|
||||
|
||||
## Upgrades configurations from the state they were in at version 4.1 to the
|
||||
# state they should be in at version 4.2.
|
||||
class VersionUpgrade41to42(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 4.1 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 4.1 format only
|
||||
# and needs to be included in the version upgrade system rather than
|
||||
# globally in Uranium.
|
||||
#
|
||||
# \param serialised The serialised form of a CFG file.
|
||||
# \return The version number stored in the CFG file.
|
||||
# \raises ValueError The format of the version number in the file is
|
||||
# incorrect.
|
||||
# \raises KeyError The format of the file is incorrect.
|
||||
def getCfgVersion(self, serialised: str) -> int:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades instance containers to have the new version
|
||||
# number.
|
||||
#
|
||||
# This renames the renamed settings in the containers.
|
||||
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
# Update version number.
|
||||
parser["metadata"]["setting_version"] = "8"
|
||||
|
||||
# Certain instance containers (such as definition changes) reference to a certain definition container
|
||||
# Since a number of those changed name, we also need to update those.
|
||||
old_definition = parser["general"]["definition"]
|
||||
if old_definition in _renamed_profiles:
|
||||
parser["general"]["definition"] = _renamed_profiles[old_definition]
|
||||
|
||||
# Rename settings.
|
||||
if "values" in parser:
|
||||
for old_name, new_name in _renamed_settings.items():
|
||||
if old_name in parser["values"]:
|
||||
parser["values"][new_name] = parser["values"][old_name]
|
||||
del parser["values"][old_name]
|
||||
# Remove settings.
|
||||
for key in _removed_settings:
|
||||
if key in parser["values"]:
|
||||
del parser["values"][key]
|
||||
|
||||
# For quality-changes profiles made for Creality printers, change the definition to the creality_base and make sure that the quality is something we have a profile for.
|
||||
if parser["metadata"].get("type", "") == "quality_changes":
|
||||
for possible_printer in _quality_changes_to_creality_base:
|
||||
if os.path.basename(filename).startswith(possible_printer + "_"):
|
||||
parser["general"]["definition"] = "creality_base"
|
||||
parser["metadata"]["quality_type"] = _creality_limited_quality_type.get(parser["metadata"]["quality_type"], "draft")
|
||||
break
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
|
||||
## Upgrades Preferences to have the new version number.
|
||||
#
|
||||
# This renames the renamed settings in the list of visible settings.
|
||||
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
# Update version number.
|
||||
parser["metadata"]["setting_version"] = "8"
|
||||
|
||||
# Renamed settings.
|
||||
if "visible_settings" in parser["general"]:
|
||||
visible_settings = parser["general"]["visible_settings"]
|
||||
visible_setting_set = set(visible_settings.split(";"))
|
||||
for old_name, new_name in _renamed_settings.items():
|
||||
if old_name in visible_setting_set:
|
||||
visible_setting_set.remove(old_name)
|
||||
visible_setting_set.add(new_name)
|
||||
for removed_key in _removed_settings:
|
||||
if removed_key in visible_setting_set:
|
||||
visible_setting_set.remove(removed_key)
|
||||
parser["general"]["visible_settings"] = ";".join(visible_setting_set)
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
|
||||
## Upgrades stacks to have the new version number.
|
||||
def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
# Update version number.
|
||||
parser["metadata"]["setting_version"] = "8"
|
||||
|
||||
# Change renamed profiles.
|
||||
if "containers" in parser:
|
||||
# For legacy Creality printers, change the variant to 0.4.
|
||||
definition_id = parser["containers"]["6"]
|
||||
if parser["metadata"].get("type", "machine") == "extruder_train":
|
||||
if parser["containers"]["4"] == "empty_variant": # Necessary for people entering from CreawsomeMod who already had a variant.
|
||||
if definition_id in _default_variants:
|
||||
parser["containers"]["4"] = _default_variants[definition_id]
|
||||
if definition_id == "creality_cr10_extruder_0": # We can't disambiguate between Creality CR-10 and Creality-CR10S since they share the same extruder definition. Have to go by the name.
|
||||
if "cr-10s" in parser["metadata"].get("machine", "Creality CR-10").lower(): # Not perfect, since the user can change this name :(
|
||||
parser["containers"]["4"] = "creality_cr10s_0.4"
|
||||
|
||||
# Also change the quality to go along with it.
|
||||
material_id = parser["containers"]["3"]
|
||||
old_quality_id = parser["containers"]["2"]
|
||||
if material_id in _creality_quality_per_material and old_quality_id in _creality_quality_per_material[material_id]:
|
||||
parser["containers"]["2"] = _creality_quality_per_material[material_id][old_quality_id]
|
||||
|
||||
stack_copy = {} # type: Dict[str, str] # Make a copy so that we don't modify the dict we're iterating over.
|
||||
stack_copy.update(parser["containers"])
|
||||
for position, profile_id in stack_copy.items():
|
||||
if profile_id in _renamed_profiles:
|
||||
parser["containers"][position] = _renamed_profiles[profile_id]
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
59
plugins/VersionUpgrade/VersionUpgrade41to42/__init__.py
Normal file
59
plugins/VersionUpgrade/VersionUpgrade41to42/__init__.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any, Dict, TYPE_CHECKING
|
||||
|
||||
from . import VersionUpgrade41to42
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Application import Application
|
||||
|
||||
upgrade = VersionUpgrade41to42.VersionUpgrade41to42()
|
||||
|
||||
def getMetaData() -> Dict[str, Any]:
|
||||
return {
|
||||
"version_upgrade": {
|
||||
# From To Upgrade function
|
||||
("preferences", 6000007): ("preferences", 6000008, upgrade.upgradePreferences),
|
||||
("machine_stack", 4000007): ("machine_stack", 4000008, upgrade.upgradeStack),
|
||||
("extruder_train", 4000007): ("extruder_train", 4000008, upgrade.upgradeStack),
|
||||
("definition_changes", 4000007): ("definition_changes", 4000008, upgrade.upgradeInstanceContainer),
|
||||
("quality_changes", 4000007): ("quality_changes", 4000008, upgrade.upgradeInstanceContainer),
|
||||
("quality", 4000007): ("quality", 4000008, upgrade.upgradeInstanceContainer),
|
||||
("user", 4000007): ("user", 4000008, upgrade.upgradeInstanceContainer),
|
||||
},
|
||||
"sources": {
|
||||
"preferences": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"."}
|
||||
},
|
||||
"machine_stack": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./machine_instances"}
|
||||
},
|
||||
"extruder_train": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./extruders"}
|
||||
},
|
||||
"definition_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./definition_changes"}
|
||||
},
|
||||
"quality_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality_changes"}
|
||||
},
|
||||
"quality": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality"}
|
||||
},
|
||||
"user": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./user"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def register(app: "Application") -> Dict[str, Any]:
|
||||
return { "version_upgrade": upgrade }
|
8
plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json
Normal file
8
plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "Version Upgrade 4.1 to 4.2",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 4.1 to Cura 4.2.",
|
||||
"api": "6.0",
|
||||
"i18n-catalog": "cura"
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
[shaders]
|
||||
vertex =
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
attribute highp vec4 a_vertex;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
}
|
||||
|
||||
fragment =
|
||||
|
@ -19,13 +21,15 @@ fragment =
|
|||
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewMatrix;
|
||||
uniform highp mat4 u_projectionMatrix;
|
||||
|
||||
in highp vec4 a_vertex;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * a_vertex;
|
||||
}
|
||||
|
||||
fragment41core =
|
||||
|
@ -43,7 +47,9 @@ fragment41core =
|
|||
u_color = [0.02, 0.02, 0.02, 1.0]
|
||||
|
||||
[bindings]
|
||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewMatrix = view_matrix
|
||||
u_projectionMatrix = projection_matrix
|
||||
|
||||
[attributes]
|
||||
a_vertex = vertex
|
||||
|
|
|
@ -63,9 +63,19 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
Logger.log("w", "Can't change metadata {key} of material {material_id} because it's read-only.".format(key = key, material_id = self.getId()))
|
||||
return
|
||||
|
||||
# Some metadata such as diameter should also be instantiated to be a setting. Go though all values for the
|
||||
# "properties" field and apply the new values to SettingInstances as well.
|
||||
new_setting_values_dict = {}
|
||||
if key == "properties":
|
||||
for k, v in value.items():
|
||||
if k in self.__material_properties_setting_map:
|
||||
new_setting_values_dict[self.__material_properties_setting_map[k]] = v
|
||||
|
||||
# Prevent recursion
|
||||
if not apply_to_all:
|
||||
super().setMetaDataEntry(key, value)
|
||||
for k, v in new_setting_values_dict.items():
|
||||
self.setProperty(k, "value", v)
|
||||
return
|
||||
|
||||
# Get the MaterialGroup
|
||||
|
@ -74,17 +84,23 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
material_group = material_manager.getMaterialGroup(root_material_id)
|
||||
if not material_group: #If the profile is not registered in the registry but loose/temporary, it will not have a base file tree.
|
||||
super().setMetaDataEntry(key, value)
|
||||
for k, v in new_setting_values_dict.items():
|
||||
self.setProperty(k, "value", v)
|
||||
return
|
||||
# Update the root material container
|
||||
root_material_container = material_group.root_material_node.getContainer()
|
||||
if root_material_container is not None:
|
||||
root_material_container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
for k, v in new_setting_values_dict.items():
|
||||
root_material_container.setProperty(k, "value", v)
|
||||
|
||||
# Update all containers derived from it
|
||||
for node in material_group.derived_material_node_list:
|
||||
container = node.getContainer()
|
||||
if container is not None:
|
||||
container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
for k, v in new_setting_values_dict.items():
|
||||
container.setProperty(k, "value", v)
|
||||
|
||||
## Overridden from InstanceContainer, similar to setMetaDataEntry.
|
||||
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
|
||||
|
@ -174,13 +190,16 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
## End Name Block
|
||||
|
||||
for key, value in metadata.items():
|
||||
builder.start(key) # type: ignore
|
||||
key_to_use = key
|
||||
if key in self._metadata_tags_that_have_cura_namespace:
|
||||
key_to_use = "cura:" + key_to_use
|
||||
builder.start(key_to_use) # type: ignore
|
||||
if value is not None: #Nones get handled well by the builder.
|
||||
#Otherwise the builder always expects a string.
|
||||
#Deserialize expects the stringified version.
|
||||
value = str(value)
|
||||
builder.data(value)
|
||||
builder.end(key)
|
||||
builder.end(key_to_use)
|
||||
|
||||
builder.end("metadata")
|
||||
## End Metadata Block
|
||||
|
@ -950,7 +969,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
machine_compatibility = cls._parseCompatibleValue(entry.text)
|
||||
|
||||
for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces):
|
||||
machine_id_list = product_id_map.get(identifier.get("product"), [])
|
||||
machine_id_list = product_id_map.get(identifier.get("product", ""), [])
|
||||
if not machine_id_list:
|
||||
machine_id_list = cls.getPossibleDefinitionIDsFromName(identifier.get("product"))
|
||||
|
||||
|
@ -982,7 +1001,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
result_metadata.append(new_material_metadata)
|
||||
|
||||
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
|
||||
buildplate_map = {} # type: Dict[str, Dict[str, bool]]
|
||||
buildplate_map = {} # type: Dict[str, Dict[str, bool]]
|
||||
buildplate_map["buildplate_compatible"] = {}
|
||||
buildplate_map["buildplate_recommended"] = {}
|
||||
for buildplate in buildplates:
|
||||
|
@ -1167,6 +1186,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
def __str__(self):
|
||||
return "<XmlMaterialProfile '{my_id}' ('{name}') from base file '{base_file}'>".format(my_id = self.getId(), name = self.getName(), base_file = self.getMetaDataEntry("base_file"))
|
||||
|
||||
_metadata_tags_that_have_cura_namespace = {"pva_compatible", "breakaway_compatible"}
|
||||
|
||||
# Map XML file setting names to internal names
|
||||
__material_settings_setting_map = {
|
||||
"print temperature": "default_material_print_temperature",
|
||||
|
@ -1180,6 +1201,13 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
"surface energy": "material_surface_energy",
|
||||
"shrinkage percentage": "material_shrinkage_percentage",
|
||||
"build volume temperature": "build_volume_temperature",
|
||||
"anti ooze retract position": "material_anti_ooze_retracted_position",
|
||||
"anti ooze retract speed": "material_anti_ooze_retraction_speed",
|
||||
"break preparation position": "material_break_preparation_retracted_position",
|
||||
"break preparation speed": "material_break_preparation_speed",
|
||||
"break position": "material_break_retracted_position",
|
||||
"break speed": "material_break_speed",
|
||||
"break temperature": "material_break_temperature"
|
||||
}
|
||||
__unmapped_settings = [
|
||||
"hardware compatible",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue