Merge branch 'master' into feature_preheat_extruder

This commit is contained in:
fieldOfView 2018-01-17 17:36:40 +01:00
commit 6e0717a967
58 changed files with 806 additions and 328 deletions

View file

@ -81,8 +81,10 @@ class ThreeMFReader(MeshReader):
self._object_count += 1
node_name = "Object %s" % self._object_count
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
um_node = CuraSceneNode()
um_node.addDecorator(BuildPlateDecorator(0))
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
um_node.setName(node_name)
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
um_node.setTransformation(transformation)

View file

@ -168,11 +168,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("w", "Unknown definition container type %s for %s",
definition_container_type, each_definition_container_file)
Job.yieldThread()
# sanity check
if machine_definition_container_count != 1:
msg = "Expecting one machine definition container but got %s" % machine_definition_container_count
Logger.log("e", msg)
raise RuntimeError(msg)
return WorkspaceReader.PreReadResult.failed #Not a workspace file but ordinary 3MF.
material_labels = []
material_conflict = False
@ -271,7 +269,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# if the global stack is found, we check if there are conflicts in the extruder stacks
if containers_found_dict["machine"] and not machine_conflict:
for extruder_stack_file in extruder_stack_files:
container_id = self._stripFileToId(extruder_stack_file)
serialized = archive.open(extruder_stack_file).read().decode("utf-8")
parser = configparser.ConfigParser()
parser.read_string(serialized)
@ -303,7 +300,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
break
num_visible_settings = 0
has_visible_settings_string = False
try:
temp_preferences = Preferences()
serialized = archive.open("Cura/preferences.cfg").read().decode("utf-8")
@ -609,7 +605,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
instance_container.setName(self._container_registry.uniqueName(instance_container.getName()))
new_changes_container_id = self.getNewId(instance_container.getId())
instance_container._id = new_changes_container_id
instance_container.setMetaDataEntry("id", new_changes_container_id)
# TODO: we don't know the following is correct or not, need to verify
# AND REFACTOR!!!

View file

@ -6,8 +6,9 @@ from UM.Math.Vector import Vector
from UM.Logger import Logger
from UM.Math.Matrix import Matrix
from UM.Application import Application
import UM.Scene.SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
import Savitar
@ -62,11 +63,15 @@ class ThreeMFWriter(MeshWriter):
self._store_archive = store_archive
## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
# \returns Uranium Scenen node.
# \returns Uranium Scene node.
def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()):
if type(um_node) not in [UM.Scene.SceneNode.SceneNode, CuraSceneNode]:
if not isinstance(um_node, SceneNode):
return None
active_build_plate_nr = CuraApplication.getInstance().getBuildPlateModel().activeBuildPlate
if um_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
return
savitar_node = Savitar.SceneNode()
node_matrix = um_node.getLocalTransformation()
@ -97,6 +102,9 @@ class ThreeMFWriter(MeshWriter):
savitar_node.setSetting(key, str(stack.getProperty(key, "value")))
for child_node in um_node.getChildren():
# only save the nodes on the active build plate
if child_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
continue
savitar_child_node = self._convertUMNodeToSavitarNode(child_node)
if savitar_child_node is not None:
savitar_node.addChild(savitar_child_node)

View file

@ -88,7 +88,7 @@ class CuraEngineBackend(QObject, Backend):
#
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
Application.getInstance().getExtruderManager().activeExtruderChanged.connect(self._onGlobalStackChanged)
Application.getInstance().getExtruderManager().extrudersAdded.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
@ -423,7 +423,7 @@ class CuraEngineBackend(QObject, Backend):
#
# \param source The scene node that was changed.
def _onSceneChanged(self, source):
if not issubclass(type(source), SceneNode):
if not isinstance(source, SceneNode):
return
build_plate_changed = set()
@ -722,7 +722,7 @@ class CuraEngineBackend(QObject, Backend):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack.containersChanged.disconnect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruders = list(self._global_container_stack.extruders.values())
for extruder in extruders:
extruder.propertyChanged.disconnect(self._onSettingChanged)
@ -733,7 +733,7 @@ class CuraEngineBackend(QObject, Backend):
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
self._global_container_stack.containersChanged.connect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruders = list(self._global_container_stack.extruders.values())
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingChanged)
extruder.containersChanged.connect(self._onChanged)

View file

@ -122,6 +122,12 @@ class StartSliceJob(Job):
self.setResult(StartJobResult.BuildPlateError)
return
# Don't slice if the buildplate or the nozzle type is incompatible with the materials
if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
not Application.getInstance().getMachineManager().variantBuildplateUsable:
self.setResult(StartJobResult.MaterialIncompatible)
return
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
material = extruder_stack.findContainer({"type": "material"})
if material:

View file

@ -225,7 +225,10 @@ class MachineSettingsAction(MachineAction):
material_approximate_diameter = str(round(material_diameter))
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
if not machine_diameter:
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
if extruder_stack.definition.hasProperty("material_diameter", "value"):
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
else:
machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value")
machine_approximate_diameter = str(round(machine_diameter))
if material_approximate_diameter != machine_approximate_diameter:

View file

@ -54,7 +54,11 @@ class PostProcessingPlugin(QObject, Extension):
## Execute all post-processing scripts on the gcode.
def execute(self, output_device):
scene = Application.getInstance().getController().getScene()
gcode_dict = getattr(scene, "gcode_dict")
gcode_dict = None
if hasattr(scene, "gcode_dict"):
gcode_dict = getattr(scene, "gcode_dict")
if not gcode_dict:
return

View file

@ -106,7 +106,7 @@ class SimulationPass(RenderPass):
nozzle_node = node
nozzle_node.setVisible(False)
elif issubclass(type(node), SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import sys
@ -344,7 +344,12 @@ class SimulationView(View):
self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
self._min_thickness = min(float(p.lineThicknesses.min()), self._min_thickness)
try:
self._min_thickness = min(float(p.lineThicknesses[numpy.nonzero(p.lineThicknesses)].min()), self._min_thickness)
except:
# Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding
# the zero) can't be calculated
Logger.log("i", "Min thickness can't be calculated because all the values are zero")
if max_layer_number < layer_id:
max_layer_number = layer_id
if min_layer_number > layer_id:

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
@ -117,7 +117,7 @@ class SimulationViewProxy(QObject):
def setSimulationViewType(self, layer_view_type):
active_view = self._controller.getActiveView()
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
active_view.setSimulationViewisinstance(layer_view_type)
active_view.setSimulationViewType(layer_view_type)
@pyqtSlot(result=int)
def getSimulationViewType(self):

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtQml import qmlRegisterSingletonType
@ -18,7 +18,7 @@ def getMetaData():
}
def createSimulationViewProxy(engine, script_engine):
return SimulationViewProxy.SimulatorViewProxy()
return SimulationViewProxy.SimulationViewProxy()
def register(app):
simulation_view = SimulationView.SimulationView()

View file

@ -52,13 +52,19 @@ Component
{
id: addRemovePrintersLabel
anchors.right: parent.right
text: "Add / remove printers"
text: catalog.i18nc("@label link to connect manager", "Add/Remove printers")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
}
MouseArea
{
anchors.fill: addRemovePrintersLabel
hoverEnabled: true
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
onEntered: addRemovePrintersLabel.font.underline = true
onExited: addRemovePrintersLabel.font.underline = false
}
}

View file

@ -69,6 +69,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self.setShortDescription(i18n_catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print over network"))
self.setDescription(i18n_catalog.i18nc("@properties:tooltip", "Print over network"))
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network"))
self._printer_uuid_to_unique_name_mapping = {}
self._finished_jobs = []
@ -76,20 +78,26 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._cluster_size = int(properties.get(b"cluster_size", 0))
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
self.writeStarted.emit(self)
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list", [])
if not self._gcode:
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
# Unable to find g-code. Nothing to send
return
self._gcode = gcode_list
if len(self._printers) > 1:
self._spawnPrinterSelectionDialog()
else:
self.sendPrintJob()
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
def _spawnPrinterSelectionDialog(self):
if self._printer_selection_dialog is None:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PrintWindow.qml")
@ -240,7 +248,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
newly_finished_jobs = [job for job in finished_jobs if job not in self._finished_jobs and job.owner == username]
for job in newly_finished_jobs:
job_completed_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:
job_completed_text = i18n_catalog.i18nc("@info:status", "Printer '{printer_name}' has finished printing '{job_name}'.".format(printer_name=job.assignedPrinter.name, job_name = job.name))
else:
job_completed_text = i18n_catalog.i18nc("@info:status", "The print job '{job_name}' was finished.".format(job_name = job.name))
job_completed_message = Message(text=job_completed_text, title = i18n_catalog.i18nc("@info:status", "Print finished"))
job_completed_message.show()

View file

@ -78,10 +78,16 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def _onAuthenticationStateChanged(self):
# We only accept commands if we are authenticated.
self._setAcceptsCommands(self._authentication_state == AuthState.Authenticated)
if self._authentication_state == AuthState.Authenticated:
self._setAcceptsCommands(True)
else:
self._setAcceptsCommands(False)
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network."))
elif self._authentication_state == AuthState.AuthenticationRequested:
self.setConnectionText(i18n_catalog.i18nc("@info:status",
"Connected over the network. Please approve the access request on the printer."))
elif self._authentication_state == AuthState.AuthenticationDenied:
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network. No access to control the printer."))
def _setupMessages(self):
self._authentication_requested_message = Message(i18n_catalog.i18nc("@info:status",
@ -175,15 +181,18 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# Not authenticated, so unable to send job.
return
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
self.writeStarted.emit(self)
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list", [])
if not self._gcode:
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
# Unable to find g-code. Nothing to send
return
self._gcode = gcode_list
errors = self._checkForErrors()
if errors:
text = i18n_catalog.i18nc("@label", "Unable to start a new print job.")
@ -229,6 +238,9 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# No warnings or errors, so we're good to go.
self._startPrint()
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
def _startPrint(self):
Logger.log("i", "Sending print job to printer.")
if self._sending_gcode:

View file

@ -31,6 +31,7 @@ UM.Dialog
property var printersModel: ListModel{}
function resetPrintersModel() {
printersModel.clear()
printersModel.append({ name: "Automatic", key: ""})
for (var index in OutputDevice.printers)

View file

@ -115,24 +115,8 @@ Item
{
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura")
text: catalog.i18nc("@action:button", "Activate Configuration")
visible: printerConnected && !isClusterPrinter()
visible: false // printerConnected && !isClusterPrinter()
onClicked: manager.loadConfigurationFromPrinter()
function isClusterPrinter() {
return false
//TODO: Hardcoded this for the moment now. These info components might also need to move.
/*if(Cura.MachineManager.printerOutputDevices.length == 0)
{
return false;
}
var clusterSize = Cura.MachineManager.printerOutputDevices[0].clusterSize;
// This is not a cluster printer or the cluster it is just one printer
if(clusterSize == undefined || clusterSize == 1)
{
return false;
}
return true;*/
}
}
}

View file

@ -80,6 +80,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._firmware_progress = 0
self._firmware_update_state = FirmwareUpdateState.idle
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
# Queue for commands that need to be send. Used when command is sent when a print is active.
self._command_queue = Queue()

View file

@ -576,6 +576,42 @@ class XmlMaterialProfile(InstanceContainer):
if is_new_material:
containers_to_add.append(new_material)
# Find the buildplates compatibility
buildplates = machine.iterfind("./um:buildplate", self.__namespaces)
buildplate_map = {}
buildplate_map["buildplate_compatible"] = {}
buildplate_map["buildplate_recommended"] = {}
for buildplate in buildplates:
buildplate_id = buildplate.get("id")
if buildplate_id is None:
continue
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
id = buildplate_id)
if not variant_containers:
# It is not really properly defined what "ID" is so also search for variants by name.
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
definition = machine_id, name = buildplate_id)
if not variant_containers:
continue
buildplate_compatibility = machine_compatibility
buildplate_recommended = machine_compatibility
settings = buildplate.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__unmapped_settings:
if key == "hardware compatible":
buildplate_compatibility = self._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = self._parseCompatibleValue(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
hotends = machine.iterfind("./um:hotend", self.__namespaces)
for hotend in hotends:
hotend_id = hotend.get("id")
@ -605,8 +641,7 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent
# material
# Same as machine compatibility, keep the derived material containers consistent with the parent material
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
is_new_material = False
@ -623,6 +658,9 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
new_hotend_material.getMetaData()["definition"] = machine_id
if buildplate_map["buildplate_compatible"]:
new_hotend_material.getMetaData()["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
cached_hotend_setting_properties.update(hotend_setting_values)
@ -763,6 +801,34 @@ class XmlMaterialProfile(InstanceContainer):
if len(found_materials) == 0: #This is a new material.
result_metadata.append(new_material_metadata)
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
buildplate_map = {}
buildplate_map["buildplate_compatible"] = {}
buildplate_map["buildplate_recommended"] = {}
for buildplate in buildplates:
buildplate_id = buildplate.get("id")
if buildplate_id is None:
continue
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
if not variant_containers:
# It is not really properly defined what "ID" is so also search for variants by name.
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
if not variant_containers:
continue
settings = buildplate.iterfind("./um:setting", cls.__namespaces)
for entry in settings:
key = entry.get("key")
if key == "hardware compatible":
buildplate_compatibility = cls._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = cls._parseCompatibleValue(entry.text)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_map["buildplate_compatible"]
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_map["buildplate_recommended"]
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
hotend_id = hotend.get("id")
if hotend_id is None:
@ -781,8 +847,7 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent
# material
# Same as machine compatibility, keep the derived material containers consistent with the parent material
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
if found_materials:
new_hotend_material_metadata = found_materials[0]
@ -799,6 +864,9 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
new_hotend_material_metadata["id"] = new_hotend_id
new_hotend_material_metadata["definition"] = machine_id
if buildplate_map["buildplate_compatible"]:
new_hotend_material_metadata["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
new_hotend_material_metadata["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
if len(found_materials) == 0:
result_metadata.append(new_hotend_material_metadata)
@ -874,7 +942,7 @@ class XmlMaterialProfile(InstanceContainer):
# Map XML file setting names to internal names
__material_settings_setting_map = {
"print temperature": "default_material_print_temperature",
"heated bed temperature": "material_bed_temperature",
"heated bed temperature": "default_material_bed_temperature",
"standby temperature": "material_standby_temperature",
"processing temperature graph": "material_flow_temp_graph",
"print cooling": "cool_fan_speed",
@ -884,7 +952,8 @@ class XmlMaterialProfile(InstanceContainer):
"surface energy": "material_surface_energy"
}
__unmapped_settings = [
"hardware compatible"
"hardware compatible",
"hardware recommended"
]
__material_properties_setting_map = {
"diameter": "material_diameter"