diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 41337c5345..86c9933573 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -240,9 +240,11 @@ class BuildVolume(SceneNode): self._active_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) self._width = self._active_container_stack.getProperty("machine_width", "value") + machine_height = self._active_container_stack.getProperty("machine_height", "value") if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": - self._height = self._active_container_stack.getProperty("gantry_height", "value") - self._buildVolumeMessage() + self._height = min(self._active_container_stack.getProperty("gantry_height", "value"), machine_height) + if self._height < machine_height: + self._buildVolumeMessage() else: self._height = self._active_container_stack.getProperty("machine_height", "value") self._depth = self._active_container_stack.getProperty("machine_depth", "value") @@ -258,9 +260,11 @@ class BuildVolume(SceneNode): rebuild_me = False if setting_key == "print_sequence": + machine_height = self._active_container_stack.getProperty("machine_height", "value") if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time": - self._height = self._active_container_stack.getProperty("gantry_height", "value") - self._buildVolumeMessage() + self._height = min(self._active_container_stack.getProperty("gantry_height", "value"), machine_height) + if self._height < machine_height: + self._buildVolumeMessage() else: self._height = self._active_container_stack.getProperty("machine_height", "value") rebuild_me = True diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py new file mode 100644 index 0000000000..a37aa9c5cb --- /dev/null +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -0,0 +1,93 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from PyQt5.QtCore import pyqtSlot + +from cura.MachineAction import MachineAction +import cura.Settings.CuraContainerRegistry + +import UM.Application +import UM.Settings.InstanceContainer +import UM.Settings.DefinitionContainer +import UM.Logger + +import UM.i18n +catalog = UM.i18n.i18nCatalog("cura") + +class MachineSettingsAction(MachineAction): + def __init__(self, parent = None): + super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings")) + self._qml_url = "MachineSettingsAction.qml" + + cura.Settings.CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) + + def _reset(self): + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + if global_container_stack: + variant = global_container_stack.findContainer({"type": "variant"}) + if variant and variant.getId() == "empty_variant": + variant_index = global_container_stack.getContainerIndex(variant) + self._createVariant(global_container_stack, variant_index) + + def _createVariant(self, global_container_stack, variant_index): + # Create and switch to a variant to store the settings in + new_variant = UM.Settings.InstanceContainer(global_container_stack.getName() + "_variant") + new_variant.addMetaDataEntry("type", "variant") + new_variant.setDefinition(global_container_stack.getBottom()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant) + global_container_stack.replaceContainer(variant_index, new_variant) + + def _onContainerAdded(self, container): + # Add this action as a supported action to all machine definitions + if isinstance(container, UM.Settings.DefinitionContainer) and container.getMetaDataEntry("type") == "machine": + if container.getProperty("machine_extruder_count", "value") > 1: + # Multiextruder printers are not currently supported + UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Multi-extrusion printers are not supported", container.getId()) + return + if container.getMetaDataEntry("has_variants", False): + # Machines that use variants are not currently supported + UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Machines that use variants are not supported", container.getId()) + return + + UM.Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey()) + + @pyqtSlot() + def forceUpdate(self): + # Force rebuilding the build volume by reloading the global container stack. + # This is a bit of a hack, but it seems quick enough. + UM.Application.getInstance().globalContainerStackChanged.emit() + + @pyqtSlot() + def updateHasMaterialsMetadata(self): + # Updates the has_materials metadata flag after switching gcode flavor + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + if global_container_stack: + definition = global_container_stack.getBottom() + if definition.getProperty("machine_gcode_flavor", "value") == "UltiGCode" and not definition.getMetaDataEntry("has_materials", False): + has_materials = global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode" + + material_container = global_container_stack.findContainer({"type": "material"}) + material_index = global_container_stack.getContainerIndex(material_container) + + if has_materials: + if "has_materials" in global_container_stack.getMetaData(): + global_container_stack.setMetaDataEntry("has_materials", True) + else: + global_container_stack.addMetaDataEntry("has_materials", True) + + # Set the material container to a sane default + if material_container.getId() == "empty_material": + search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*" } + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + global_container_stack.replaceContainer(material_index, containers[0]) + else: + # The metadata entry is stored in an ini, and ini files are parsed as strings only. + # Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False. + if "has_materials" in global_container_stack.getMetaData(): + global_container_stack.removeMetaDataEntry("has_materials") + + empty_material = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0] + global_container_stack.replaceContainer(material_index, empty_material) + + UM.Application.getInstance().globalContainerStackChanged.emit() \ No newline at end of file diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml new file mode 100644 index 0000000000..fd7bb58e97 --- /dev/null +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -0,0 +1,476 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Cura.MachineAction +{ + anchors.fill: parent; + Item + { + id: bedLevelMachineAction + anchors.fill: parent; + + UM.I18nCatalog { id: catalog; name: "cura"; } + + Label + { + id: pageTitle + width: parent.width + text: catalog.i18nc("@title", "Machine Settings") + wrapMode: Text.WordWrap + font.pointSize: 18; + } + Label + { + id: pageDescription + anchors.top: pageTitle.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + width: parent.width + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Please enter the correct settings for your printer below:") + } + + Column + { + height: parent.height - y + width: parent.width - UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").height + + anchors.left: parent.left + anchors.top: pageDescription.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + + Row + { + width: parent.width + spacing: UM.Theme.getSize("default_margin").height + + Column + { + width: parent.width / 2 + spacing: UM.Theme.getSize("default_margin").height + + Label + { + text: catalog.i18nc("@label", "Printer Settings") + font.bold: true + } + + Grid + { + columns: 3 + columnSpacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "X (Width)") + } + TextField + { + id: buildAreaWidthField + text: machineWidthProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineWidthProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y (Depth)") + } + TextField + { + id: buildAreaDepthField + text: machineDepthProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineDepthProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Z (Height)") + } + TextField + { + id: buildAreaHeightField + text: machineHeightProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineHeightProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + } + + Column + { + CheckBox + { + id: heatedBedCheckBox + text: catalog.i18nc("@option:check", "Heated Bed") + checked: String(machineHeatedBedProvider.properties.value).toLowerCase() != 'false' + onClicked: machineHeatedBedProvider.setPropertyValue("value", checked) + } + CheckBox + { + id: centerIsZeroCheckBox + text: catalog.i18nc("@option:check", "Machine Center is Zero") + checked: String(machineCenterIsZeroProvider.properties.value).toLowerCase() != 'false' + onClicked: machineCenterIsZeroProvider.setPropertyValue("value", checked) + } + } + + Row + { + spacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "GCode Flavor") + } + + ComboBox + { + model: ["RepRap (Marlin/Sprinter)", "UltiGCode"] + currentIndex: machineGCodeFlavorProvider.properties.value != model[1] ? 0 : 1 + onActivated: + { + machineGCodeFlavorProvider.setPropertyValue("value", model[index]); + manager.updateHasMaterialsMetadata(); + } + } + } + } + + Column + { + width: parent.width / 2 + spacing: UM.Theme.getSize("default_margin").height + + Label + { + text: catalog.i18nc("@label", "Printhead Settings") + font.bold: true + } + + Grid + { + columns: 3 + columnSpacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "X min") + } + TextField + { + id: printheadXMinField + text: getHeadPolygonCoord("x", "min") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y min") + } + TextField + { + id: printheadYMinField + text: getHeadPolygonCoord("y", "min") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "X max") + } + TextField + { + id: printheadXMaxField + text: getHeadPolygonCoord("x", "max") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y max") + } + TextField + { + id: printheadYMaxField + text: getHeadPolygonCoord("y", "max") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + + Label + { + text: catalog.i18nc("@label", "Gantry height") + } + TextField + { + id: gantryHeightField + text: gantryHeightProvider.properties.value + validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ } + onEditingFinished: { gantryHeightProvider.setPropertyValue("value", text) } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + + Label + { + text: catalog.i18nc("@label", "Nozzle size") + } + TextField + { + id: nozzleSizeField + text: machineNozzleSizeProvider.properties.value + validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ } + onEditingFinished: { machineNozzleSizeProvider.setPropertyValue("value", text) } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + } + } + } + + Row + { + spacing: UM.Theme.getSize("default_margin").width + anchors.left: parent.left + anchors.right: parent.right + height: parent.height - y + Column + { + height: parent.height + width: parent.width / 2 + Label + { + text: catalog.i18nc("@label", "Start Gcode") + } + TextArea + { + id: machineStartGcodeField + width: parent.width + height: parent.height - y + text: machineStartGcodeProvider.properties.value + onActiveFocusChanged: + { + if(!activeFocus) + { + machineStartGcodeProvider.setPropertyValue("value", machineStartGcodeField.text) + } + } + } + } + Column { + height: parent.height + width: parent.width / 2 + Label + { + text: catalog.i18nc("@label", "End Gcode") + } + TextArea + { + id: machineEndGcodeField + width: parent.width + height: parent.height - y + text: machineEndGcodeProvider.properties.value + onActiveFocusChanged: + { + if(!activeFocus) + { + machineEndGcodeProvider.setPropertyValue("value", machineEndGcodeField.text) + } + } + } + } + } + } + } + + function getHeadPolygonCoord(axis, minMax) + { + var polygon = JSON.parse(machineHeadPolygonProvider.properties.value); + var item = (axis == "x") ? 0 : 1 + var result = polygon[0][item]; + for(var i = 1; i < polygon.length; i++) { + if (minMax == "min") { + result = Math.min(result, polygon[i][item]); + } else { + result = Math.max(result, polygon[i][item]); + } + } + return Math.abs(result); + } + + function setHeadPolygon() + { + var polygon = []; + polygon.push([-parseFloat(printheadXMinField.text), parseFloat(printheadYMaxField.text)]); + polygon.push([-parseFloat(printheadXMinField.text),-parseFloat(printheadYMinField.text)]); + polygon.push([ parseFloat(printheadXMaxField.text), parseFloat(printheadYMaxField.text)]); + polygon.push([ parseFloat(printheadXMaxField.text),-parseFloat(printheadYMinField.text)]); + machineHeadPolygonProvider.setPropertyValue("value", JSON.stringify(polygon)); + manager.forceUpdate(); + } + + UM.SettingPropertyProvider + { + id: machineWidthProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_width" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineDepthProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_depth" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineHeightProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_height" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBedProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineCenterIsZeroProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_center_is_zero" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineGCodeFlavorProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_gcode_flavor" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineNozzleSizeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_nozzle_size" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: gantryHeightProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "gantry_height" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineHeadPolygonProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_head_with_fans_polygon" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + + UM.SettingPropertyProvider + { + id: machineStartGcodeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_start_gcode" + watchedProperties: [ "value" ] + storeIndex: 3 + } + + UM.SettingPropertyProvider + { + id: machineEndGcodeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_end_gcode" + watchedProperties: [ "value" ] + storeIndex: 3 + } + +} \ No newline at end of file diff --git a/plugins/MachineSettingsAction/__init__.py b/plugins/MachineSettingsAction/__init__.py new file mode 100644 index 0000000000..7d8b253d33 --- /dev/null +++ b/plugins/MachineSettingsAction/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from . import MachineSettingsAction + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "plugin": { + "name": catalog.i18nc("@label", "Machine Settings action"), + "author": "fieldOfView", + "version": "1.0", + "description": catalog.i18nc("@info:whatsthis", "Provides a way to change machine settings (such as build volume, nozzle size, etc)"), + "api": 3 + } + } + +def register(app): + return { "machine_action": MachineSettingsAction.MachineSettingsAction() } diff --git a/resources/definitions/custom.def.json b/resources/definitions/custom.def.json new file mode 100644 index 0000000000..0ccd2d9890 --- /dev/null +++ b/resources/definitions/custom.def.json @@ -0,0 +1,16 @@ +{ + "id": "custom", + "version": 2, + "name": "Custom FDM printer", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Custom", + "category": "Custom", + "file_formats": "text/x-gcode", + "has_materials": true, + "preferred_material": "*pla*", + "first_start_actions": ["MachineSettingsAction"] + } +}