mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'master' into feature_setting_visibility_profiles
This commit is contained in:
commit
4c66c935af
52 changed files with 1347 additions and 1067 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
.git
|
||||
.github
|
||||
resources/materials
|
||||
CuraEngine
|
5
.github/ISSUE_TEMPLATE.md
vendored
5
.github/ISSUE_TEMPLATE.md
vendored
|
@ -6,7 +6,7 @@ Before filing, PLEASE check if the issue already exists (either open or closed)
|
|||
Also, please note the application version in the title of the issue. For example: "[3.2.1] Cannot connect to 3rd-party printer". Please do not write thigns like "Request:" or "[BUG]" in the title; this is what labels are for.
|
||||
|
||||
It is also helpful to attach a project (.3mf or .curaproject) file and Cura log file so we can debug issues quicker.
|
||||
Information about how to find the log file can be found at https://github.com/Ultimaker/Cura/wiki/Cura-Preferences-and-Settings-Locations. To upload a project, we recommend http://wetransfer.com, but other file hosts like Google Drive or Dropbox work well too.
|
||||
Information about how to find the log file can be found at https://github.com/Ultimaker/Cura/wiki/Cura-Preferences-and-Settings-Locations. To upload a project, try changing the extension to e.g. .curaproject.3mf.zip so that github accepts uploading the file. Otherwise we recommend http://wetransfer.com, but other file hosts like Google Drive or Dropbox work well too.
|
||||
|
||||
Thank you for using Cura!
|
||||
-->
|
||||
|
@ -26,6 +26,9 @@ Thank you for using Cura!
|
|||
**Display Driver**
|
||||
<!-- Video driver name and version -->
|
||||
|
||||
**Printer**
|
||||
<!-- Which printer was selected in Cura. Please attach project file as .curaproject.3mf.zip -->
|
||||
|
||||
**Steps to Reproduce**
|
||||
<!-- Add the steps needed that lead up to the issue (replace this text) -->
|
||||
|
||||
|
|
45
Dockerfile
Normal file
45
Dockerfile
Normal file
|
@ -0,0 +1,45 @@
|
|||
FROM ultimaker/cura-build-environment:1
|
||||
|
||||
# Environment vars for easy configuration
|
||||
ENV CURA_APP_DIR=/srv/cura
|
||||
|
||||
# Ensure our sources dir exists
|
||||
RUN mkdir $CURA_APP_DIR
|
||||
|
||||
# Setup CuraEngine
|
||||
ENV CURA_ENGINE_BRANCH=master
|
||||
WORKDIR $CURA_APP_DIR
|
||||
RUN git clone -b $CURA_ENGINE_BRANCH --depth 1 https://github.com/Ultimaker/CuraEngine
|
||||
WORKDIR $CURA_APP_DIR/CuraEngine
|
||||
RUN mkdir build
|
||||
WORKDIR $CURA_APP_DIR/CuraEngine/build
|
||||
RUN cmake3 ..
|
||||
RUN make
|
||||
RUN make install
|
||||
|
||||
# TODO: setup libCharon
|
||||
|
||||
# Setup Uranium
|
||||
ENV URANIUM_BRANCH=master
|
||||
WORKDIR $CURA_APP_DIR
|
||||
RUN git clone -b $URANIUM_BRANCH --depth 1 https://github.com/Ultimaker/Uranium
|
||||
|
||||
# Setup materials
|
||||
ENV MATERIALS_BRANCH=master
|
||||
WORKDIR $CURA_APP_DIR
|
||||
RUN git clone -b $MATERIALS_BRANCH --depth 1 https://github.com/Ultimaker/fdm_materials materials
|
||||
|
||||
# Setup Cura
|
||||
WORKDIR $CURA_APP_DIR/Cura
|
||||
ADD . .
|
||||
RUN mv $CURA_APP_DIR/materials resources/materials
|
||||
|
||||
# Make sure Cura can find CuraEngine
|
||||
RUN ln -s /usr/local/bin/CuraEngine $CURA_APP_DIR/Cura
|
||||
|
||||
# Run Cura
|
||||
WORKDIR $CURA_APP_DIR/Cura
|
||||
ENV PYTHONPATH=${PYTHONPATH}:$CURA_APP_DIR/Uranium
|
||||
RUN chmod +x ./CuraEngine
|
||||
RUN chmod +x ./run_in_docker.sh
|
||||
CMD "./run_in_docker.sh"
|
|
@ -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 platform
|
||||
|
@ -14,10 +14,11 @@ import urllib.request
|
|||
import urllib.error
|
||||
import shutil
|
||||
|
||||
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl
|
||||
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QUrl
|
||||
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
#Type hinting.
|
||||
from typing import Dict
|
||||
|
||||
|
@ -55,6 +56,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from cura.Settings.MachineNameValidator import MachineNameValidator
|
||||
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||
from cura.Machines.Models.BuildPlateModel import BuildPlateModel
|
||||
from cura.Machines.Models.NozzleModel import NozzleModel
|
||||
from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
|
||||
|
@ -211,11 +213,11 @@ class CuraApplication(QtApplication):
|
|||
|
||||
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
|
||||
{
|
||||
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
||||
("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
|
||||
("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
|
||||
("user", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("quality_changes", QualityChangesGroup.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
||||
("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
|
||||
("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
|
||||
("user", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("definition_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.DefinitionChangesContainer, "application/x-uranium-instancecontainer"),
|
||||
}
|
||||
)
|
||||
|
@ -1599,7 +1601,7 @@ class CuraApplication(QtApplication):
|
|||
result = workspace_reader.preRead(file_path, show_dialog=False)
|
||||
return result == WorkspaceReader.PreReadResult.accepted
|
||||
except Exception as e:
|
||||
Logger.log("e", "Could not check file %s: %s", file_url, e)
|
||||
Logger.logException("e", "Could not check file %s: %s", file_url)
|
||||
return False
|
||||
|
||||
def _onContextMenuRequested(self, x: float, y: float) -> None:
|
||||
|
|
|
@ -102,6 +102,13 @@ class MaterialManager(QObject):
|
|||
# GUID -> material group list
|
||||
self._guid_material_groups_map = defaultdict(list)
|
||||
for root_material_id, material_group in self._material_group_map.items():
|
||||
# This can happen when we are updating with incomplete data.
|
||||
if material_group.root_material_node is None:
|
||||
Logger.log("e", "Missing root material node for [%s]. Probably caused by update using incomplete data."
|
||||
" Check all related signals for further debugging.",
|
||||
material_group.name)
|
||||
self._update_timer.start()
|
||||
return
|
||||
guid = material_group.root_material_node.metadata["GUID"]
|
||||
self._guid_material_groups_map[guid].append(material_group)
|
||||
|
||||
|
@ -210,6 +217,7 @@ class MaterialManager(QObject):
|
|||
self.materialsUpdated.emit()
|
||||
|
||||
def _updateMaps(self):
|
||||
Logger.log("i", "Updating material lookup data ...")
|
||||
self.initialize()
|
||||
|
||||
def _onContainerMetadataChanged(self, container):
|
||||
|
@ -349,10 +357,10 @@ class MaterialManager(QObject):
|
|||
else:
|
||||
return None
|
||||
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", extruder_variant_name: str) -> Optional["MaterialNode"]:
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", extruder_variant_name: Optional[str]) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
machine_definition = global_stack.definition
|
||||
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
|
||||
if parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||
material_diameter = machine_definition.getProperty("material_diameter", "value")
|
||||
if isinstance(material_diameter, SettingFunction):
|
||||
material_diameter = material_diameter(global_stack)
|
||||
|
@ -363,6 +371,16 @@ class MaterialManager(QObject):
|
|||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
def removeMaterialByRootId(self, root_material_id: str):
|
||||
material_group = self.getMaterialGroup(root_material_id)
|
||||
if not material_group:
|
||||
Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
|
||||
return
|
||||
|
||||
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
||||
for node in nodes_to_remove:
|
||||
self._container_registry.removeContainer(node.metadata["id"])
|
||||
|
||||
#
|
||||
# Methods for GUI
|
||||
#
|
||||
|
@ -386,14 +404,7 @@ class MaterialManager(QObject):
|
|||
@pyqtSlot("QVariant")
|
||||
def removeMaterial(self, material_node: "MaterialNode"):
|
||||
root_material_id = material_node.metadata["base_file"]
|
||||
material_group = self.getMaterialGroup(root_material_id)
|
||||
if not material_group:
|
||||
Logger.log("d", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
|
||||
return
|
||||
|
||||
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
||||
for node in nodes_to_remove:
|
||||
self._container_registry.removeContainer(node.metadata["id"])
|
||||
self.removeMaterialByRootId(root_material_id)
|
||||
|
||||
#
|
||||
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
|
||||
|
@ -25,6 +26,8 @@ class BaseMaterialsModel(ListModel):
|
|||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self._application = Application.getInstance()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self.addRoleName(self.RootMaterialIdRole, "root_material_id")
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
|
@ -35,12 +38,31 @@ class BaseMaterialsModel(ListModel):
|
|||
self.addRoleName(self.ContainerNodeRole, "container_node")
|
||||
|
||||
self._extruder_position = 0
|
||||
self._extruder_stack = None
|
||||
|
||||
def _updateExtruderStack(self):
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if global_stack is None:
|
||||
return
|
||||
|
||||
if self._extruder_stack is not None:
|
||||
self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
|
||||
self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
|
||||
if self._extruder_stack is not None:
|
||||
self._extruder_stack.pyqtContainersChanged.connect(self._update)
|
||||
|
||||
def setExtruderPosition(self, position: int):
|
||||
if self._extruder_position != position:
|
||||
self._extruder_position = position
|
||||
self._updateExtruderStack()
|
||||
self.extruderPositionChanged.emit()
|
||||
|
||||
@pyqtProperty(int, fset = setExtruderPosition, notify = extruderPositionChanged)
|
||||
def extruderPosition(self) -> int:
|
||||
return self._extruder_positoin
|
||||
return self._extruder_position
|
||||
|
||||
#
|
||||
# This is an abstract method that needs to be implemented by
|
||||
#
|
||||
def _update(self):
|
||||
pass
|
||||
|
|
|
@ -120,12 +120,19 @@ class BrandMaterialsModel(ListModel):
|
|||
material_type_item = {"name": material_type,
|
||||
"colors": BaseMaterialsModel(self)}
|
||||
material_type_item["colors"].clear()
|
||||
|
||||
# Sort materials by name
|
||||
material_list = sorted(material_list, key = lambda x: x["name"].upper())
|
||||
material_type_item["colors"].setItems(material_list)
|
||||
|
||||
material_type_item_list.append(material_type_item)
|
||||
|
||||
# Sort material type by name
|
||||
material_type_item_list = sorted(material_type_item_list, key = lambda x: x["name"].upper())
|
||||
brand_item["materials"].setItems(material_type_item_list)
|
||||
|
||||
brand_item_list.append(brand_item)
|
||||
|
||||
# Sort brand by name
|
||||
brand_item_list = sorted(brand_item_list, key = lambda x: x["name"].upper())
|
||||
self.setItems(brand_item_list)
|
||||
|
|
|
@ -23,7 +23,7 @@ class CustomQualityProfilesDropDownMenuModel(QualityProfilesDropDownMenuModel):
|
|||
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(active_global_stack)
|
||||
|
||||
item_list = []
|
||||
for key in sorted(quality_changes_group_dict):
|
||||
for key in sorted(quality_changes_group_dict, key = lambda name: name.upper()):
|
||||
quality_changes_group = quality_changes_group_dict[key]
|
||||
|
||||
item = {"name": quality_changes_group.name,
|
||||
|
|
|
@ -55,6 +55,6 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
|||
item_list.append(item)
|
||||
|
||||
# Sort the item list by material name alphabetically
|
||||
item_list = sorted(item_list, key = lambda d: d["name"])
|
||||
item_list = sorted(item_list, key = lambda d: d["name"].upper())
|
||||
|
||||
self.setItems(item_list)
|
||||
|
|
|
@ -97,5 +97,5 @@ class MaterialManagementModel(ListModel):
|
|||
|
||||
material_list.append(item)
|
||||
|
||||
material_list = sorted(material_list, key = lambda k: (k["brand"].lower(), k["name"]))
|
||||
material_list = sorted(material_list, key = lambda k: (k["brand"].upper(), k["name"].upper()))
|
||||
self.setItems(material_list)
|
||||
|
|
|
@ -45,7 +45,7 @@ class NozzleModel(ListModel):
|
|||
return
|
||||
|
||||
item_list = []
|
||||
for hotend_name, container_node in sorted(variant_node_dict.items(), key = lambda i: i[0]):
|
||||
for hotend_name, container_node in sorted(variant_node_dict.items(), key = lambda i: i[0].upper()):
|
||||
item = {"id": hotend_name,
|
||||
"hotend_name": hotend_name,
|
||||
"container_node": container_node
|
||||
|
|
|
@ -59,7 +59,7 @@ class QualityManagementModel(ListModel):
|
|||
"quality_changes_group": None}
|
||||
item_list.append(item)
|
||||
# Sort by quality names
|
||||
item_list = sorted(item_list, key = lambda x: x["name"])
|
||||
item_list = sorted(item_list, key = lambda x: x["name"].upper())
|
||||
|
||||
# Create quality_changes group items
|
||||
quality_changes_item_list = []
|
||||
|
@ -74,7 +74,7 @@ class QualityManagementModel(ListModel):
|
|||
quality_changes_item_list.append(item)
|
||||
|
||||
# Sort quality_changes items by names and append to the item list
|
||||
quality_changes_item_list = sorted(quality_changes_item_list, key = lambda x: x["name"])
|
||||
quality_changes_item_list = sorted(quality_changes_item_list, key = lambda x: x["name"].upper())
|
||||
item_list += quality_changes_item_list
|
||||
|
||||
self.setItems(item_list)
|
||||
|
|
|
@ -69,7 +69,7 @@ class QualitySettingsModel(ListModel):
|
|||
return self._selected_quality_item
|
||||
|
||||
def _update(self):
|
||||
if self._selected_quality_item is None:
|
||||
if not self._selected_quality_item:
|
||||
self.setItems([])
|
||||
return
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ from .QualityGroup import QualityGroup
|
|||
|
||||
|
||||
class QualityChangesGroup(QualityGroup):
|
||||
## The file format version of quality changes.
|
||||
Version = 3
|
||||
|
||||
def __init__(self, name: str, quality_type: str, parent = None):
|
||||
super().__init__(name, quality_type, parent)
|
||||
|
|
|
@ -19,8 +19,6 @@ class ObjectsModel(ListModel):
|
|||
|
||||
self._build_plate_number = -1
|
||||
|
||||
self._stacks_have_errors = None # type:Optional[bool]
|
||||
|
||||
def setActiveBuildPlate(self, nr):
|
||||
self._build_plate_number = nr
|
||||
self._update()
|
||||
|
@ -69,11 +67,3 @@ class ObjectsModel(ListModel):
|
|||
@staticmethod
|
||||
def createObjectsModel():
|
||||
return ObjectsModel()
|
||||
|
||||
## Check if none of the model's stacks contain error states
|
||||
# The setting applied for the settings per model
|
||||
def stacksHaveErrors(self) -> bool:
|
||||
return bool(self._stacks_have_errors)
|
||||
|
||||
def setStacksHaveErrors(self, value):
|
||||
self._stacks_have_errors = value
|
|
@ -103,32 +103,32 @@ class PrinterOutputModel(QObject):
|
|||
self._head_position = Vector(x, y, z)
|
||||
self.headPositionChanged.emit()
|
||||
|
||||
@pyqtProperty("long", "long", "long")
|
||||
@pyqtProperty("long", "long", "long", "long")
|
||||
@pyqtProperty(float, float, float)
|
||||
@pyqtProperty(float, float, float, float)
|
||||
def setHeadPosition(self, x, y, z, speed = 3000):
|
||||
self.updateHeadPosition(x, y, z)
|
||||
self._controller.setHeadPosition(self, x, y, z, speed)
|
||||
|
||||
@pyqtProperty("long")
|
||||
@pyqtProperty("long", "long")
|
||||
@pyqtProperty(float)
|
||||
@pyqtProperty(float, float)
|
||||
def setHeadX(self, x, speed = 3000):
|
||||
self.updateHeadPosition(x, self._head_position.y, self._head_position.z)
|
||||
self._controller.setHeadPosition(self, x, self._head_position.y, self._head_position.z, speed)
|
||||
|
||||
@pyqtProperty("long")
|
||||
@pyqtProperty("long", "long")
|
||||
@pyqtProperty(float)
|
||||
@pyqtProperty(float, float)
|
||||
def setHeadY(self, y, speed = 3000):
|
||||
self.updateHeadPosition(self._head_position.x, y, self._head_position.z)
|
||||
self._controller.setHeadPosition(self, self._head_position.x, y, self._head_position.z, speed)
|
||||
|
||||
@pyqtProperty("long")
|
||||
@pyqtProperty("long", "long")
|
||||
@pyqtProperty(float)
|
||||
@pyqtProperty(float, float)
|
||||
def setHeadZ(self, z, speed = 3000):
|
||||
self.updateHeadPosition(self._head_position.x, self._head_position.y, z)
|
||||
self._controller.setHeadPosition(self, self._head_position.x, self._head_position.y, z, speed)
|
||||
|
||||
@pyqtSlot("long", "long", "long")
|
||||
@pyqtSlot("long", "long", "long", "long")
|
||||
@pyqtSlot(float, float, float)
|
||||
@pyqtSlot(float, float, float, float)
|
||||
def moveHead(self, x = 0, y = 0, z = 0, speed = 3000):
|
||||
self._controller.moveHead(self, x, y, z, speed)
|
||||
|
||||
|
|
|
@ -389,8 +389,6 @@ class ContainerManager(QObject):
|
|||
return ContainerManager.getInstance()
|
||||
|
||||
def _performMerge(self, merge_into, merge, clear_settings = True):
|
||||
assert isinstance(merge, type(merge_into))
|
||||
|
||||
if merge == merge_into:
|
||||
return
|
||||
|
||||
|
|
|
@ -306,6 +306,14 @@ class MachineManager(QObject):
|
|||
|
||||
self.__emitChangedSignals()
|
||||
|
||||
@staticmethod
|
||||
def getMachine(definition_id: str) -> Optional["GlobalStack"]:
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
for machine in machines:
|
||||
if machine.definition.getId() == definition_id:
|
||||
return machine
|
||||
return None
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def addMachine(self, name: str, definition_id: str) -> None:
|
||||
new_stack = CuraStackBuilder.createMachine(name, definition_id)
|
||||
|
@ -873,7 +881,7 @@ class MachineManager(QObject):
|
|||
quality_node = quality_group.nodes_for_extruders.get(position)
|
||||
|
||||
quality_changes_container = self._empty_quality_changes_container
|
||||
quality_container = self._empty_quality_changes_container
|
||||
quality_container = self._empty_quality_container
|
||||
if quality_changes_node:
|
||||
quality_changes_container = quality_changes_node.getContainer()
|
||||
if quality_node:
|
||||
|
@ -897,11 +905,13 @@ class MachineManager(QObject):
|
|||
def _setMaterial(self, position, container_node = None):
|
||||
if container_node:
|
||||
self._global_container_stack.extruders[position].material = container_node.getContainer()
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
root_material_name = container_node.getContainer().getName()
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
root_material_id = None
|
||||
root_material_name = None
|
||||
# The _current_root_material_id is used in the MaterialMenu to see which material is selected
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
root_material_name = container_node.getContainer().getName()
|
||||
if root_material_id != self._current_root_material_id[position]:
|
||||
self._current_root_material_id[position] = root_material_id
|
||||
self._current_root_material_name[position] = root_material_name
|
||||
|
@ -917,30 +927,38 @@ class MachineManager(QObject):
|
|||
|
||||
## Update current quality type and machine after setting material
|
||||
def _updateQualityWithMaterial(self):
|
||||
current_quality = None
|
||||
Logger.log("i", "Updating quality/quality_changes due to material change")
|
||||
current_quality_type = None
|
||||
if self._current_quality_group:
|
||||
current_quality = self._current_quality_group.quality_type
|
||||
quality_manager = Application.getInstance()._quality_manager
|
||||
candidate_quality_groups = quality_manager.getQualityGroups(self._global_container_stack)
|
||||
current_quality_type = self._current_quality_group.quality_type
|
||||
candidate_quality_groups = self._quality_manager.getQualityGroups(self._global_container_stack)
|
||||
available_quality_types = {qt for qt, g in candidate_quality_groups.items() if g.is_available}
|
||||
|
||||
Logger.log("d", "Current quality type = [%s]", current_quality_type)
|
||||
if not self.activeMaterialsCompatible():
|
||||
Logger.log("i", "Active materials are not compatible, setting all qualities to empty (Not Supported).")
|
||||
self._setEmptyQuality()
|
||||
return
|
||||
|
||||
if not available_quality_types:
|
||||
Logger.log("i", "No available quality types found, setting all qualities to empty (Not Supported).")
|
||||
self._setEmptyQuality()
|
||||
return
|
||||
|
||||
if current_quality in available_quality_types:
|
||||
self._setQualityGroup(candidate_quality_groups[current_quality], empty_quality_changes = False)
|
||||
if current_quality_type in available_quality_types:
|
||||
Logger.log("i", "Current available quality type [%s] is available, applying changes.", current_quality_type)
|
||||
self._setQualityGroup(candidate_quality_groups[current_quality_type], empty_quality_changes = False)
|
||||
return
|
||||
|
||||
# The current quality type is not available so we use the preferred quality type if it's available,
|
||||
# otherwise use one of the available quality types.
|
||||
quality_type = sorted(list(available_quality_types))[0]
|
||||
preferred_quality_type = self._global_container_stack.getMetaDataEntry("preferred_quality_type")
|
||||
if preferred_quality_type in available_quality_types:
|
||||
quality_type = preferred_quality_type
|
||||
|
||||
Logger.log("i", "The current quality type [%s] is not available, switching to [%s] instead",
|
||||
current_quality_type, quality_type)
|
||||
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
|
||||
|
||||
def _updateMaterialWithVariant(self, position: Optional[str]):
|
||||
|
@ -955,9 +973,8 @@ class MachineManager(QObject):
|
|||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_variant_name = extruder.variant.getMetaDataEntry("name")
|
||||
|
||||
material_manager = Application.getInstance()._material_manager
|
||||
material_diameter = self._global_container_stack.getProperty("material_diameter", "value")
|
||||
candidate_materials = material_manager.getAvailableMaterials(
|
||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||
self._global_container_stack.definition.getId(),
|
||||
current_variant_name,
|
||||
material_diameter)
|
||||
|
@ -997,28 +1014,28 @@ class MachineManager(QObject):
|
|||
self._updateQualityWithMaterial()
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityGroup(self, quality_group):
|
||||
def setQualityGroup(self, quality_group, no_dialog = False):
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityGroup(quality_group)
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
Application.getInstance().discardOrKeepProfileChanges()
|
||||
if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||
def activeQualityGroup(self):
|
||||
return self._current_quality_group
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityChangesGroup(self, quality_changes_group):
|
||||
def setQualityChangesGroup(self, quality_changes_group, no_dialog = False):
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityChangesGroup(quality_changes_group)
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
Application.getInstance().discardOrKeepProfileChanges()
|
||||
if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
||||
def activeQualityChangesGroup(self):
|
||||
|
|
|
@ -9,8 +9,7 @@ from UM.Signal import Signal, signalemitter
|
|||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM.Application import Application
|
||||
|
||||
from cura.Settings.PerObjectContainerStack import PerObjectContainerStack
|
||||
|
@ -40,10 +39,6 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
|
||||
|
||||
self._is_non_printing_mesh = False
|
||||
self._error_check_timer = QTimer()
|
||||
self._error_check_timer.setInterval(250)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
self._error_check_timer.timeout.connect(self._checkStackForErrors)
|
||||
|
||||
self._stack.propertyChanged.connect(self._onSettingChanged)
|
||||
|
||||
|
@ -99,21 +94,9 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||
# Trigger slice/need slicing if the value has changed.
|
||||
if property_name == "value":
|
||||
self._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
|
||||
if not self._is_non_printing_mesh:
|
||||
# self._error_check_timer.start()
|
||||
self._checkStackForErrors()
|
||||
Application.getInstance().getBackend().needsSlicing()
|
||||
Application.getInstance().getBackend().tickle()
|
||||
|
||||
def _checkStackForErrors(self):
|
||||
hasErrors = False;
|
||||
for key in self._stack.getAllKeys():
|
||||
validation_state = self._stack.getProperty(key, "validationState")
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
Logger.log("w", "Setting Per Object %s is not valid.", key)
|
||||
hasErrors = True
|
||||
break
|
||||
Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors)
|
||||
Application.getInstance().getBackend().needsSlicing()
|
||||
Application.getInstance().getBackend().tickle()
|
||||
|
||||
## Makes sure that the stack upon which the container stack is placed is
|
||||
# kept up to date.
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,7 +52,6 @@ class WorkspaceDialog(QObject):
|
|||
|
||||
machineConflictChanged = pyqtSignal()
|
||||
qualityChangesConflictChanged = pyqtSignal()
|
||||
definitionChangesConflictChanged = pyqtSignal()
|
||||
materialConflictChanged = pyqtSignal()
|
||||
numVisibleSettingsChanged = pyqtSignal()
|
||||
activeModeChanged = pyqtSignal()
|
||||
|
@ -196,10 +195,6 @@ class WorkspaceDialog(QObject):
|
|||
def qualityChangesConflict(self):
|
||||
return self._has_quality_changes_conflict
|
||||
|
||||
@pyqtProperty(bool, notify=definitionChangesConflictChanged)
|
||||
def definitionChangesConflict(self):
|
||||
return self._has_definition_changes_conflict
|
||||
|
||||
@pyqtProperty(bool, notify=materialConflictChanged)
|
||||
def materialConflict(self):
|
||||
return self._has_material_conflict
|
||||
|
@ -229,18 +224,11 @@ class WorkspaceDialog(QObject):
|
|||
self._has_quality_changes_conflict = quality_changes_conflict
|
||||
self.qualityChangesConflictChanged.emit()
|
||||
|
||||
def setDefinitionChangesConflict(self, definition_changes_conflict):
|
||||
if self._has_definition_changes_conflict != definition_changes_conflict:
|
||||
self._has_definition_changes_conflict = definition_changes_conflict
|
||||
self.definitionChangesConflictChanged.emit()
|
||||
|
||||
def getResult(self):
|
||||
if "machine" in self._result and not self._has_machine_conflict:
|
||||
self._result["machine"] = None
|
||||
if "quality_changes" in self._result and not self._has_quality_changes_conflict:
|
||||
self._result["quality_changes"] = None
|
||||
if "definition_changes" in self._result and not self._has_definition_changes_conflict:
|
||||
self._result["definition_changes"] = None
|
||||
if "material" in self._result and not self._has_material_conflict:
|
||||
self._result["material"] = None
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Workspace.WorkspaceWriter import WorkspaceWriter
|
||||
import configparser
|
||||
from io import StringIO
|
||||
import zipfile
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
import zipfile
|
||||
from io import StringIO
|
||||
import configparser
|
||||
from UM.Workspace.WorkspaceWriter import WorkspaceWriter
|
||||
|
||||
|
||||
class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
||||
|
@ -16,7 +17,10 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
super().__init__()
|
||||
|
||||
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
|
||||
mesh_writer = Application.getInstance().getMeshFileHandler().getWriter("3MFWriter")
|
||||
application = Application.getInstance()
|
||||
machine_manager = application.getMachineManager()
|
||||
|
||||
mesh_writer = application.getMeshFileHandler().getWriter("3MFWriter")
|
||||
|
||||
if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace
|
||||
return False
|
||||
|
@ -29,17 +33,17 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
if archive is None: # This happens if there was no mesh data to write.
|
||||
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
|
||||
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = machine_manager.activeMachine
|
||||
|
||||
# Add global container stack data to the archive.
|
||||
self._writeContainerToArchive(global_container_stack, archive)
|
||||
self._writeContainerToArchive(global_stack, archive)
|
||||
|
||||
# Also write all containers in the stack to the file
|
||||
for container in global_container_stack.getContainers():
|
||||
for container in global_stack.getContainers():
|
||||
self._writeContainerToArchive(container, archive)
|
||||
|
||||
# Check if the machine has extruders and save all that data as well.
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()):
|
||||
for extruder_stack in global_stack.extruders.values():
|
||||
self._writeContainerToArchive(extruder_stack, archive)
|
||||
for container in extruder_stack.getContainers():
|
||||
self._writeContainerToArchive(container, archive)
|
||||
|
@ -59,9 +63,9 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
version_file = zipfile.ZipInfo("Cura/version.ini")
|
||||
version_config_parser = configparser.ConfigParser(interpolation = None)
|
||||
version_config_parser.add_section("versions")
|
||||
version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion())
|
||||
version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType())
|
||||
version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode()))
|
||||
version_config_parser.set("versions", "cura_version", application.getVersion())
|
||||
version_config_parser.set("versions", "build_type", application.getBuildType())
|
||||
version_config_parser.set("versions", "is_debug_mode", str(application.getIsDebugMode()))
|
||||
|
||||
version_file_string = StringIO()
|
||||
version_config_parser.write(version_file_string)
|
||||
|
@ -85,7 +89,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
# Some containers have a base file, which should then be the file to use.
|
||||
if "base_file" in container.getMetaData():
|
||||
base_file = container.getMetaDataEntry("base_file")
|
||||
container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
|
||||
if base_file != container.getId():
|
||||
container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
|
||||
|
||||
file_name = "Cura/%s.%s" % (container.getId(), file_suffix)
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
[3.2.1]
|
||||
*Bug fixes
|
||||
- Fixed issues where Cura crashes on startup and loading profiles
|
||||
- Updated translations
|
||||
- Fixed an issue where the text would not render properly
|
||||
|
||||
[3.2.0]
|
||||
*Tree support
|
||||
Experimental tree-like support structure that uses ‘branches’ to support prints. Branches ‘grow’ and multiply towards the model, with fewer contact points than alternative support methods. This results in better surface finishes for organic-shaped prints.
|
||||
|
|
|
@ -33,6 +33,9 @@ from UM.i18n import i18nCatalog
|
|||
catalog = i18nCatalog("cura")
|
||||
|
||||
class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
backendError = Signal()
|
||||
|
||||
## Starts the back-end plug-in.
|
||||
#
|
||||
# This registers all the signal listeners and prepares for communication
|
||||
|
@ -289,6 +292,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
return
|
||||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
|
||||
|
@ -297,6 +301,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
return
|
||||
|
@ -325,6 +330,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
return
|
||||
|
@ -347,6 +353,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
return
|
||||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.BuildPlateError:
|
||||
|
@ -355,6 +362,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
|
||||
|
@ -364,6 +372,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
self._invokeSlice()
|
||||
|
|
|
@ -136,14 +136,10 @@ class StartSliceJob(Job):
|
|||
self.setResult(StartJobResult.MaterialIncompatible)
|
||||
return
|
||||
|
||||
# Validate settings per selectable model
|
||||
if Application.getInstance().getObjectsModel().stacksHaveErrors():
|
||||
self.setResult(StartJobResult.ObjectSettingError)
|
||||
return
|
||||
|
||||
# Don't slice if there is a per object setting with an error value.
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.isSelectable():
|
||||
if not isinstance(node, CuraSceneNode) or not node.isSelectable():
|
||||
continue
|
||||
|
||||
if self._checkStackForErrors(node.callDecoration("getStack")):
|
||||
|
|
|
@ -2,20 +2,16 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
|
||||
import UM.i18n
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
from cura.MachineAction import MachineAction
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Logger import Logger
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.MachineAction import MachineAction
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
import UM.i18n
|
||||
catalog = UM.i18n.i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -26,6 +22,8 @@ class MachineSettingsAction(MachineAction):
|
|||
super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings"))
|
||||
self._qml_url = "MachineSettingsAction.qml"
|
||||
|
||||
self._application = Application.getInstance()
|
||||
|
||||
self._global_container_stack = None
|
||||
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
|
@ -34,38 +32,44 @@ class MachineSettingsAction(MachineAction):
|
|||
self._container_registry = ContainerRegistry.getInstance()
|
||||
self._container_registry.containerAdded.connect(self._onContainerAdded)
|
||||
self._container_registry.containerRemoved.connect(self._onContainerRemoved)
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
|
||||
self._empty_container = self._container_registry.getEmptyInstanceContainer()
|
||||
self._backend = self._application.getBackend()
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
self._empty_definition_container_id_list = []
|
||||
|
||||
def _isEmptyDefinitionChanges(self, container_id: str):
|
||||
if not self._empty_definition_container_id_list:
|
||||
self._empty_definition_container_id_list = [self._application.empty_container.getId(),
|
||||
self._application.empty_definition_changes_container.getId()]
|
||||
return container_id in self._empty_definition_container_id_list
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
# Add this action as a supported action to all machine definitions
|
||||
if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine":
|
||||
Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
|
||||
self._application.getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
|
||||
|
||||
def _onContainerRemoved(self, container):
|
||||
# Remove definition_changes containers when a stack is removed
|
||||
if container.getMetaDataEntry("type") in ["machine", "extruder_train"]:
|
||||
definition_changes_container = container.definitionChanges
|
||||
if definition_changes_container == self._empty_container:
|
||||
definition_changes_id = container.definitionChanges.getId()
|
||||
if self._isEmptyDefinitionChanges(definition_changes_id):
|
||||
return
|
||||
|
||||
self._container_registry.removeContainer(definition_changes_container.getId())
|
||||
self._container_registry.removeContainer(definition_changes_id)
|
||||
|
||||
def _reset(self):
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
# Make sure there is a definition_changes container to store the machine settings
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
if definition_changes_container == self._empty_container:
|
||||
definition_changes_container = CuraStackBuilder.createDefinitionChangesContainer(
|
||||
self._global_container_stack, self._global_container_stack.getName() + "_settings")
|
||||
definition_changes_id = self._global_container_stack.definitionChanges.getId()
|
||||
if self._isEmptyDefinitionChanges(definition_changes_id):
|
||||
CuraStackBuilder.createDefinitionChangesContainer(self._global_container_stack,
|
||||
self._global_container_stack.getName() + "_settings")
|
||||
|
||||
# Notify the UI in which container to store the machine settings data
|
||||
from cura.Settings.CuraContainerStack import CuraContainerStack, _ContainerIndexes
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
|
||||
container_index = _ContainerIndexes.DefinitionChanges
|
||||
if container_index != self._container_index:
|
||||
|
@ -107,13 +111,13 @@ class MachineSettingsAction(MachineAction):
|
|||
def setMachineExtruderCount(self, extruder_count):
|
||||
# Note: this method was in this class before, but since it's quite generic and other plugins also need it
|
||||
# it was moved to the machine manager instead. Now this method just calls the machine manager.
|
||||
Application.getInstance().getMachineManager().setActiveMachineExtruderCount(extruder_count)
|
||||
self._application.getMachineManager().setActiveMachineExtruderCount(extruder_count)
|
||||
|
||||
@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.
|
||||
Application.getInstance().globalContainerStackChanged.emit()
|
||||
self._application.globalContainerStackChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def updateHasMaterialsMetadata(self):
|
||||
|
@ -126,9 +130,11 @@ class MachineSettingsAction(MachineAction):
|
|||
# In other words: only continue for the UM2 (extended), but not for the UM2+
|
||||
return
|
||||
|
||||
stacks = ExtruderManager.getInstance().getExtruderStacks()
|
||||
machine_manager = self._application.getMachineManager()
|
||||
extruder_positions = list(self._global_container_stack.extruders.keys())
|
||||
has_materials = self._global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode"
|
||||
|
||||
material_node = None
|
||||
if has_materials:
|
||||
if "has_materials" in self._global_container_stack.getMetaData():
|
||||
self._global_container_stack.setMetaDataEntry("has_materials", True)
|
||||
|
@ -136,26 +142,22 @@ class MachineSettingsAction(MachineAction):
|
|||
self._global_container_stack.addMetaDataEntry("has_materials", True)
|
||||
|
||||
# Set the material container for each extruder to a sane default
|
||||
for stack in stacks:
|
||||
material_container = stack.material
|
||||
if material_container == self._empty_container:
|
||||
machine_approximate_diameter = str(round(self._global_container_stack.getProperty("material_diameter", "value")))
|
||||
search_criteria = { "type": "material", "definition": "fdmprinter", "id": self._global_container_stack.getMetaDataEntry("preferred_material"), "approximate_diameter": machine_approximate_diameter}
|
||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
||||
if materials:
|
||||
stack.material = materials[0]
|
||||
material_manager = self._application.getMaterialManager()
|
||||
material_node = material_manager.getDefaultMaterial(self._global_container_stack, None)
|
||||
|
||||
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 self._global_container_stack.getMetaData():
|
||||
self._global_container_stack.removeMetaDataEntry("has_materials")
|
||||
|
||||
for stack in stacks:
|
||||
stack.material = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
# set materials
|
||||
for position in extruder_positions:
|
||||
machine_manager.setMaterial(position, material_node)
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.emit()
|
||||
self._application.globalContainerStackChanged.emit()
|
||||
|
||||
@pyqtSlot(int)
|
||||
def updateMaterialForDiameter(self, extruder_position: int):
|
||||
# Updates the material container to a material that matches the material diameter set for the printer
|
||||
Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)
|
||||
self._application.getExtruderManager().updateMaterialForDiameter(extruder_position)
|
||||
|
|
|
@ -25,20 +25,7 @@ UM.TooltipArea
|
|||
|
||||
onClicked:
|
||||
{
|
||||
// Important first set visible and then subscribe
|
||||
// otherwise the setting is not yet in list
|
||||
// For unsubscribe is important first remove the subscription and then
|
||||
// set as invisible
|
||||
if(checked)
|
||||
{
|
||||
addedSettingsModel.setVisible(model.key, checked);
|
||||
UM.ActiveTool.triggerActionWithData("subscribeForSettingValidation", model.key)
|
||||
}
|
||||
else
|
||||
{
|
||||
UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key)
|
||||
addedSettingsModel.setVisible(model.key, checked);
|
||||
}
|
||||
addedSettingsModel.setVisible(model.key, checked);
|
||||
UM.ActiveTool.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -240,10 +240,7 @@ Item {
|
|||
width: Math.round(UM.Theme.getSize("setting").height / 2)
|
||||
height: UM.Theme.getSize("setting").height
|
||||
|
||||
onClicked: {
|
||||
addedSettingsModel.setVisible(model.key, false)
|
||||
UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key)
|
||||
}
|
||||
onClicked: addedSettingsModel.setVisible(model.key, false)
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
|
|
|
@ -10,10 +10,7 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
|||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Event import Event
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Logger import Logger
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
## This tool allows the user to add & change settings per node in the scene.
|
||||
# The settings per object are kept in a ContainerStack, which is linked to a node by decorator.
|
||||
|
@ -37,12 +34,6 @@ class PerObjectSettingsTool(Tool):
|
|||
self._onGlobalContainerChanged()
|
||||
Selection.selectionChanged.connect(self._updateEnabled)
|
||||
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
|
||||
self._error_check_timer = QTimer()
|
||||
self._error_check_timer.setInterval(250)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
|
||||
|
||||
def event(self, event):
|
||||
super().event(event)
|
||||
|
@ -151,65 +142,3 @@ class PerObjectSettingsTool(Tool):
|
|||
else:
|
||||
self._single_model_selected = True
|
||||
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode and self._single_model_selected)
|
||||
|
||||
|
||||
def _onPropertyChanged(self, key: str, property_name: str) -> None:
|
||||
if property_name == "validationState":
|
||||
# self._error_check_timer.start()
|
||||
return
|
||||
|
||||
def _updateStacksHaveErrors(self) -> None:
|
||||
return
|
||||
# self._checkStacksHaveErrors()
|
||||
|
||||
|
||||
def _checkStacksHaveErrors(self):
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
|
||||
# valdiate only objects which can be selected because the settings per object
|
||||
# can be applied only for them
|
||||
if not node.isSelectable():
|
||||
continue
|
||||
|
||||
hasErrors = self._checkStackForErrors(node.callDecoration("getStack"))
|
||||
Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors)
|
||||
|
||||
#If any of models has an error then no reason check next objects on the build plate
|
||||
if hasErrors:
|
||||
break
|
||||
|
||||
|
||||
def _checkStackForErrors(self, stack):
|
||||
print("checking for errors")
|
||||
if stack is None:
|
||||
return False
|
||||
|
||||
for key in stack.getAllKeys():
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
Logger.log("w", "Setting Per Object %s is not valid.", key)
|
||||
return True
|
||||
return False
|
||||
|
||||
def subscribeForSettingValidation(self, setting_name):
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway.
|
||||
if not stack:
|
||||
return ""
|
||||
|
||||
settings = stack.getTop()
|
||||
setting_instance = settings.getInstance(setting_name)
|
||||
if setting_instance:
|
||||
setting_instance.propertyChanged.connect(self._onPropertyChanged)
|
||||
|
||||
def unsubscribeForSettingValidation(self, setting_name):
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway.
|
||||
if not stack:
|
||||
return ""
|
||||
|
||||
settings = stack.getTop()
|
||||
setting_instance = settings.getInstance(setting_name)
|
||||
if setting_instance:
|
||||
setting_instance.propertyChanged.disconnect(self._onPropertyChanged)
|
||||
|
|
|
@ -2,17 +2,15 @@
|
|||
# under the terms of the AGPLv3 or higher
|
||||
|
||||
from ..Script import Script
|
||||
#from UM.Logger import Logger
|
||||
# from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
class ColorChange(Script):
|
||||
class FilamentChange(Script):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name":"Color Change",
|
||||
"key": "ColorChange",
|
||||
"name":"Filament Change",
|
||||
"key": "FilamentChange",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
|
@ -60,17 +58,17 @@ class ColorChange(Script):
|
|||
if later_retract is not None and later_retract > 0.:
|
||||
color_change = color_change + (" L%.2f" % later_retract)
|
||||
|
||||
color_change = color_change + " ; Generated by ColorChange plugin"
|
||||
color_change = color_change + " ; Generated by FilamentChange plugin"
|
||||
|
||||
layer_targets = layer_nums.split(',')
|
||||
layer_targets = layer_nums.split(",")
|
||||
if len(layer_targets) > 0:
|
||||
for layer_num in layer_targets:
|
||||
layer_num = int( layer_num.strip() )
|
||||
layer_num = int(layer_num.strip())
|
||||
if layer_num < len(data):
|
||||
layer = data[ layer_num - 1 ]
|
||||
layer = data[layer_num - 1]
|
||||
lines = layer.split("\n")
|
||||
lines.insert(2, color_change )
|
||||
final_line = "\n".join( lines )
|
||||
data[ layer_num - 1 ] = final_line
|
||||
lines.insert(2, color_change)
|
||||
final_line = "\n".join(lines)
|
||||
data[layer_num - 1] = final_line
|
||||
|
||||
return data
|
|
@ -93,13 +93,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._gcode = gcode_list
|
||||
|
||||
is_job_sent = True
|
||||
if len(self._printers) > 1:
|
||||
self._spawnPrinterSelectionDialog()
|
||||
else:
|
||||
self.sendPrintJob()
|
||||
is_job_sent = self.sendPrintJob()
|
||||
|
||||
# Notify the UI that a switch to the print monitor should happen
|
||||
Application.getInstance().getController().setActiveStage("MonitorStage")
|
||||
if is_job_sent:
|
||||
Application.getInstance().getController().setActiveStage("MonitorStage")
|
||||
|
||||
def _spawnPrinterSelectionDialog(self):
|
||||
if self._printer_selection_dialog is None:
|
||||
|
@ -121,7 +123,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
i18n_catalog.i18nc("@info:status",
|
||||
"Sending new jobs (temporarily) blocked, still sending the previous print job."))
|
||||
self._error_message.show()
|
||||
return
|
||||
return False
|
||||
|
||||
self._sending_gcode = True
|
||||
|
||||
|
@ -134,7 +136,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
compressed_gcode = self._compressGCode()
|
||||
if compressed_gcode is None:
|
||||
# Abort was called.
|
||||
return
|
||||
return False
|
||||
|
||||
parts = []
|
||||
|
||||
|
@ -152,6 +154,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, onFinished=self._onPostPrintJobFinished, onProgress=self._onUploadPrintJobProgress)
|
||||
|
||||
return True
|
||||
|
||||
@pyqtProperty(QObject, notify=activePrinterChanged)
|
||||
def activePrinter(self) -> Optional["PrinterOutputModel"]:
|
||||
return self._active_printer
|
||||
|
@ -194,6 +198,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
# the "reply" should be disconnected
|
||||
if self._latest_reply_handler:
|
||||
self._latest_reply_handler.disconnect()
|
||||
self._latest_reply_handler = None
|
||||
|
||||
|
||||
@pyqtSlot()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
@ -17,7 +17,7 @@ from .avr_isp import stk500v2, intelHex
|
|||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
|
||||
from serial import Serial, SerialException
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread
|
||||
from time import time, sleep
|
||||
from queue import Queue
|
||||
|
@ -266,8 +266,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
command = (command + "\n").encode()
|
||||
if not command.endswith(b"\n"):
|
||||
command += b"\n"
|
||||
self._serial.write(b"\n")
|
||||
self._serial.write(command)
|
||||
try:
|
||||
self._serial.write(b"\n")
|
||||
self._serial.write(command)
|
||||
except SerialTimeoutException:
|
||||
Logger.log("w", "Timeout when sending command to printer via USB.")
|
||||
|
||||
def _update(self):
|
||||
while self._connection_state == ConnectionState.connected and self._serial is not None:
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import List
|
||||
|
||||
from cura.MachineAction import MachineAction
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
|
@ -5,6 +10,7 @@ from UM.FlameProfiler import pyqtSlot
|
|||
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -26,38 +32,45 @@ class BedLevelMachineAction(MachineAction):
|
|||
@pyqtSlot()
|
||||
def startBedLeveling(self):
|
||||
self._bed_level_position = 0
|
||||
printer_output_devices = self._getPrinterOutputDevices()
|
||||
if printer_output_devices:
|
||||
printer_output_devices[0].homeBed()
|
||||
printer_output_devices[0].moveHead(0, 0, 3)
|
||||
printer_output_devices[0].homeHead()
|
||||
|
||||
def _getPrinterOutputDevices(self):
|
||||
printer_output_devices = self._getPrinterOutputDevices()
|
||||
if not printer_output_devices:
|
||||
Logger.log("e", "Can't start bed levelling. The printer connection seems to have been lost.")
|
||||
return
|
||||
printer = printer_output_devices[0].activePrinter
|
||||
|
||||
printer.homeBed()
|
||||
printer.moveHead(0, 0, 3)
|
||||
printer.homeHead()
|
||||
|
||||
def _getPrinterOutputDevices(self) -> List[PrinterOutputDevice]:
|
||||
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
|
||||
|
||||
@pyqtSlot()
|
||||
def moveToNextLevelPosition(self):
|
||||
output_devices = self._getPrinterOutputDevices()
|
||||
if output_devices: # We found at least one output device
|
||||
output_device = output_devices[0]
|
||||
if not output_devices: #No output devices. Can't move.
|
||||
Logger.log("e", "Can't move to the next position. The printer connection seems to have been lost.")
|
||||
return
|
||||
printer = output_devices[0].activePrinter
|
||||
|
||||
if self._bed_level_position == 0:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.homeHead()
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 1:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 2:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position >= 3:
|
||||
output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
|
||||
self.setFinished()
|
||||
if self._bed_level_position == 0:
|
||||
printer.moveHead(0, 0, 3)
|
||||
printer.homeHead()
|
||||
printer.moveHead(0, 0, 3)
|
||||
printer.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
|
||||
printer.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 1:
|
||||
printer.moveHead(0, 0, 3)
|
||||
printer.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
|
||||
printer.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 2:
|
||||
printer.moveHead(0, 0, 3)
|
||||
printer.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
|
||||
printer.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position >= 3:
|
||||
output_devices[0].sendCommand("M18") # Turn off all motors so the user can move the axes
|
||||
self.setFinished()
|
|
@ -33,6 +33,10 @@ def getMetaData():
|
|||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./extruders"}
|
||||
},
|
||||
"quality": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality"}
|
||||
},
|
||||
"quality_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality"}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To parse preference files.
|
||||
import io #To serialise the preference files afterwards.
|
||||
|
||||
from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this.
|
||||
|
||||
## Mapping extruder definition IDs to the positions that they are in.
|
||||
_EXTRUDER_TO_POSITION = {
|
||||
"builder_premium_large_front": 1,
|
||||
"builder_premium_large_rear": 0,
|
||||
"builder_premium_medium_front": 1,
|
||||
"builder_premium_medium_rear": 0,
|
||||
"builder_premium_small_front": 1,
|
||||
"builder_premium_small_rear": 0,
|
||||
"cartesio_extruder_0": 0,
|
||||
"cartesio_extruder_1": 1,
|
||||
"cartesio_extruder_2": 2,
|
||||
"cartesio_extruder_3": 3,
|
||||
"custom_extruder_1": 0, #Warning, non-programmers are attempting to count here.
|
||||
"custom_extruder_2": 1,
|
||||
"custom_extruder_3": 2,
|
||||
"custom_extruder_4": 3,
|
||||
"custom_extruder_5": 4,
|
||||
"custom_extruder_6": 5,
|
||||
"custom_extruder_7": 6,
|
||||
"custom_extruder_8": 7,
|
||||
"hBp_extruder_left": 0,
|
||||
"hBp_extruder_right": 1,
|
||||
"makeit_dual_1st": 0,
|
||||
"makeit_dual_2nd": 1,
|
||||
"makeit_l_dual_1st": 0,
|
||||
"makeit_l_dual_2nd": 1,
|
||||
"ord_extruder_0": 0,
|
||||
"ord_extruder_1": 1,
|
||||
"ord_extruder_2": 2,
|
||||
"ord_extruder_3": 3,
|
||||
"ord_extruder_4": 4,
|
||||
"punchtec_connect_xl_extruder_left": 0,
|
||||
"punchtec_connect_xl_extruder_right": 1,
|
||||
"raise3D_N2_dual_extruder_0": 0,
|
||||
"raise3D_N2_dual_extruder_1": 1,
|
||||
"raise3D_N2_plus_dual_extruder_0": 0,
|
||||
"raise3D_N2_plus_dual_extruder_1": 1,
|
||||
"ultimaker3_extended_extruder_left": 0,
|
||||
"ultimaker3_extended_extruder_right": 1,
|
||||
"ultimaker3_extruder_left": 0,
|
||||
"ultimaker3_extruder_right": 1,
|
||||
"ultimaker_original_dual_1st": 0,
|
||||
"ultimaker_original_dual_2nd": 1,
|
||||
"vertex_k8400_dual_1st": 0,
|
||||
"vertex_k8400_dual_2nd": 1
|
||||
}
|
||||
|
||||
## Upgrades configurations from the state they were in at version 3.2 to the
|
||||
# state they should be in at version 3.3.
|
||||
class VersionUpgrade32to33(VersionUpgrade):
|
||||
## Gets the version number from a CFG file in Uranium's 3.2 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.2 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):
|
||||
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 a quality changes container to the new format.
|
||||
def upgradeQualityChanges(self, serialized, filename):
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
#Extruder quality changes profiles have the extruder position instead of the ID of the extruder definition.
|
||||
if "metadata" in parser and "extruder" in parser["metadata"]: #Only do this for extruder profiles.
|
||||
extruder_id = parser["metadata"]["extruder"]
|
||||
if extruder_id in _EXTRUDER_TO_POSITION:
|
||||
extruder_position = _EXTRUDER_TO_POSITION[extruder_id]
|
||||
else:
|
||||
extruder_position = 0 #The user was using custom extruder definitions. He's on his own then.
|
||||
|
||||
parser["metadata"]["position"] = str(extruder_position)
|
||||
del parser["metadata"]["extruder"]
|
||||
|
||||
#Update version number.
|
||||
parser["general"]["version"] = "3"
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
23
plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py
Normal file
23
plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import VersionUpgrade32to33
|
||||
|
||||
upgrade = VersionUpgrade32to33.VersionUpgrade32to33()
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"version_upgrade": {
|
||||
# From To Upgrade function
|
||||
("quality_changes", 2000004): ("quality_changes", 3000004, upgrade.upgradeQualityChanges),
|
||||
},
|
||||
"sources": {
|
||||
"quality_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "version_upgrade": upgrade }
|
8
plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json
Normal file
8
plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "Version Upgrade 3.2 to 3.3",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
|
||||
"api": 4,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os.path
|
||||
|
@ -10,7 +10,7 @@ from UM.View.RenderPass import RenderPass
|
|||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
class XRayPass(RenderPass):
|
||||
|
@ -27,7 +27,7 @@ class XRayPass(RenderPass):
|
|||
|
||||
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData() and node.isVisible():
|
||||
if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible():
|
||||
batch.addItem(node.getWorldTransformation(), node.getMeshData())
|
||||
|
||||
self.bind()
|
||||
|
|
|
@ -13,9 +13,9 @@ vertex =
|
|||
}
|
||||
|
||||
fragment =
|
||||
uniform sampler2D u_layer0;
|
||||
uniform sampler2D u_layer1;
|
||||
uniform sampler2D u_layer2;
|
||||
uniform sampler2D u_layer0; //Default pass.
|
||||
uniform sampler2D u_layer1; //Selection pass.
|
||||
uniform sampler2D u_layer2; //X-ray pass.
|
||||
|
||||
uniform vec2 u_offset[9];
|
||||
|
||||
|
@ -83,9 +83,9 @@ vertex41core =
|
|||
|
||||
fragment41core =
|
||||
#version 410
|
||||
uniform sampler2D u_layer0;
|
||||
uniform sampler2D u_layer1;
|
||||
uniform sampler2D u_layer2;
|
||||
uniform sampler2D u_layer0; //Default pass.
|
||||
uniform sampler2D u_layer1; //Selection pass.
|
||||
uniform sampler2D u_layer2; //X-ray pass.
|
||||
|
||||
uniform vec2 u_offset[9];
|
||||
|
||||
|
|
|
@ -200,18 +200,25 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
## Begin Settings Block
|
||||
builder.start("settings")
|
||||
|
||||
if self.getDefinition().getId() == "fdmprinter":
|
||||
if self.getMetaDataEntry("definition") == "fdmprinter":
|
||||
for instance in self.findInstances():
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
machine_container_map = {}
|
||||
machine_nozzle_map = {}
|
||||
|
||||
variant_manager = CuraApplication.getInstance()._variant_manager
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
material_manager = CuraApplication.getInstance().getMaterialManager()
|
||||
|
||||
root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile.
|
||||
material_group = material_manager.getMaterialGroup(root_material_id)
|
||||
|
||||
all_containers = []
|
||||
for node in [material_group.root_material_node] + material_group.derived_material_node_list:
|
||||
all_containers.append(node.getContainer())
|
||||
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId())
|
||||
for container in all_containers:
|
||||
definition_id = container.getDefinition().getId()
|
||||
definition_id = container.getMetaDataEntry("definition")
|
||||
if definition_id == "fdmprinter":
|
||||
continue
|
||||
|
||||
|
@ -233,7 +240,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
product_id_map = self.getProductIdMap()
|
||||
|
||||
for definition_id, container in machine_container_map.items():
|
||||
definition = container.getDefinition()
|
||||
definition_id = container.getMetaDataEntry("definition")
|
||||
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = definition_id)[0]
|
||||
|
||||
product = definition_id
|
||||
for product_name, product_id_list in product_id_map.items():
|
||||
|
@ -243,13 +251,14 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
builder.start("machine")
|
||||
builder.start("machine_identifier", {
|
||||
"manufacturer": container.getMetaDataEntry("machine_manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")),
|
||||
"manufacturer": container.getMetaDataEntry("machine_manufacturer",
|
||||
definition_metadata.get("manufacturer", "Unknown")),
|
||||
"product": product
|
||||
})
|
||||
builder.end("machine_identifier")
|
||||
|
||||
for instance in container.findInstances():
|
||||
if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
if self.getMetaDataEntry("definition") == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
||||
continue
|
||||
|
||||
|
|
|
@ -6229,6 +6229,258 @@
|
|||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"bridge_settings_enabled":
|
||||
{
|
||||
"label": "Enable Bridge Settings",
|
||||
"description": "Detect bridges and modify print speed, flow and fan settings while bridges are printed.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"bridge_wall_min_length":
|
||||
{
|
||||
"label": "Minimum Bridge Wall Length",
|
||||
"description": "Unsupported walls shorter than this will be printed using the normal wall settings. Longer unsupported walls will be printed using the bridge wall settings.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"default_value": 5,
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"bridge_skin_support_threshold":
|
||||
{
|
||||
"label": "Bridge Skin Support Threshold",
|
||||
"description": "If a skin region is supported for less than this percentage of its area, print it using the bridge settings. Otherwise it is printed using the normal skin settings.",
|
||||
"unit": "%",
|
||||
"default_value": 50,
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_wall_max_overhang":
|
||||
{
|
||||
"label": "Bridge Wall Max Overhang",
|
||||
"description": "The maximum allowed width of the region of air below a wall line before the wall is printed using bridge settings. Expressed as a percentage of the wall line width. When the air gap is wider than this, the wall line is printed using the bridge settings. Otherwise, the wall line is printed using the normal settings. The lower the value, the more likely it is that overhung wall lines will be printed using bridge settings.",
|
||||
"unit": "%",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_wall_coast":
|
||||
{
|
||||
"label": "Bridge Wall Coasting",
|
||||
"description": "This controls the distance the extruder should coast immediately before a bridge wall begins. Coasting before the bridge starts can reduce the pressure in the nozzle and may produce a flatter bridge.",
|
||||
"unit": "%",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "500",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
"bridge_wall_speed":
|
||||
{
|
||||
"label": "Bridge Wall Speed",
|
||||
"description": "The speed at which the bridge walls are printed.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "cool_min_speed",
|
||||
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||
"maximum_value_warning": "300",
|
||||
"default_value": 15,
|
||||
"value": "max(cool_min_speed, speed_wall_0 / 2)",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_wall_material_flow":
|
||||
{
|
||||
"label": "Bridge Wall Flow",
|
||||
"description": "When printing bridge walls, the amount of material extruded is multiplied by this value.",
|
||||
"unit": "%",
|
||||
"default_value": 50,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_speed":
|
||||
{
|
||||
"label": "Bridge Skin Speed",
|
||||
"description": "The speed at which bridge skin regions are printed.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "cool_min_speed",
|
||||
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||
"maximum_value_warning": "300",
|
||||
"default_value": 15,
|
||||
"value": "max(cool_min_speed, speed_topbottom / 2)",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_material_flow":
|
||||
{
|
||||
"label": "Bridge Skin Flow",
|
||||
"description": "When printing bridge skin regions, the amount of material extruded is multiplied by this value.",
|
||||
"unit": "%",
|
||||
"default_value": 60,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_density":
|
||||
{
|
||||
"label": "Bridge Skin Density",
|
||||
"description": "The density of the bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||
"unit": "%",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"maximum_value": "100",
|
||||
"minimum_value_warning": "20",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_fan_speed":
|
||||
{
|
||||
"label": "Bridge Fan Speed",
|
||||
"description": "Percentage fan speed to use when printing bridge walls and skin.",
|
||||
"unit": "%",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_enable_more_layers":
|
||||
{
|
||||
"label": "Bridge Has Multiple Layers",
|
||||
"description": "If enabled, the second and third layers above the air are printed using the following settings. Otherwise, those layers are printed using the normal settings.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_speed_2":
|
||||
{
|
||||
"label": "Bridge Second Skin Speed",
|
||||
"description": "Print speed to use when printing the second bridge skin layer.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "cool_min_speed",
|
||||
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||
"maximum_value_warning": "300",
|
||||
"default_value": 25,
|
||||
"value": "bridge_skin_speed",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_material_flow_2":
|
||||
{
|
||||
"label": "Bridge Second Skin Flow",
|
||||
"description": "When printing the second bridge skin layer, the amount of material extruded is multiplied by this value.",
|
||||
"unit": "%",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"maximum_value": "500",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_density_2":
|
||||
{
|
||||
"label": "Bridge Second Skin Density",
|
||||
"description": "The density of the second bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||
"unit": "%",
|
||||
"default_value": 75,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"maximum_value": "100",
|
||||
"minimum_value_warning": "20",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_fan_speed_2":
|
||||
{
|
||||
"label": "Bridge Second Skin Fan Speed",
|
||||
"description": "Percentage fan speed to use when printing the second bridge skin layer.",
|
||||
"unit": "%",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 0,
|
||||
"type": "float",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_speed_3":
|
||||
{
|
||||
"label": "Bridge Third Skin Speed",
|
||||
"description": "Print speed to use when printing the third bridge skin layer.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "cool_min_speed",
|
||||
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||
"maximum_value_warning": "300",
|
||||
"default_value": 15,
|
||||
"value": "bridge_skin_speed",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_material_flow_3":
|
||||
{
|
||||
"label": "Bridge Third Skin Flow",
|
||||
"description": "When printing the third bridge skin layer, the amount of material extruded is multiplied by this value.",
|
||||
"unit": "%",
|
||||
"default_value": 110,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"maximum_value": "500",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_skin_density_3":
|
||||
{
|
||||
"label": "Bridge Third Skin Density",
|
||||
"description": "The density of the third bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||
"unit": "%",
|
||||
"default_value": 80,
|
||||
"type": "float",
|
||||
"minimum_value": "5",
|
||||
"maximum_value": "100",
|
||||
"minimum_value_warning": "20",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_fan_speed_3":
|
||||
{
|
||||
"label": "Bridge Third Skin Fan Speed",
|
||||
"description": "Percentage fan speed to use when printing the third bridge skin layer.",
|
||||
"unit": "%",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 0,
|
||||
"type": "float",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"version": 2,
|
||||
"name": "Tevo Tarantula",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "TheAssassin",
|
||||
"manufacturer": "Tevo",
|
||||
|
@ -11,62 +12,39 @@
|
|||
"platform": "prusai3_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": {
|
||||
"default_value": "Tevo Tarantula"
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
"overrides":
|
||||
{
|
||||
"machine_name": { "default_value": "Tevo Tarantula" },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_width": { "default_value": 200 },
|
||||
"machine_height": { "default_value": 200 },
|
||||
"machine_depth": { "default_value": 200 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"machine_head_polygon":
|
||||
{
|
||||
"default_value":
|
||||
[
|
||||
[-75, -18],
|
||||
[-75, 35],
|
||||
[18, 35],
|
||||
[18, -18]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_acceleration": {
|
||||
"default_value": 500
|
||||
},
|
||||
"machine_max_jerk_xy": {
|
||||
"default_value": 4.0
|
||||
},
|
||||
"machine_max_jerk_z": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"machine_max_jerk_e": {
|
||||
"default_value": 2.5
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG1 X0 Y200 F3600 ;move extruder out of the way by moving the baseplate to the front for easier access to printed object\nM84 ;steppers off"
|
||||
}
|
||||
"gantry_height": { "default_value": 55 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_acceleration": { "default_value": 2650 },
|
||||
"machine_max_jerk_xy": { "default_value": 15.0 },
|
||||
"machine_max_jerk_z": { "default_value": 0.4 },
|
||||
"machine_max_jerk_e": { "default_value": 5 },
|
||||
"machine_max_feedrate_x": { "default_value": 255 },
|
||||
"machine_max_feedrate_y": { "default_value": 225 },
|
||||
"machine_max_feedrate_z": { "default_value": 3 },
|
||||
"machine_max_acceleration_x": { "default_value": 2620 },
|
||||
"machine_max_acceleration_y": { "default_value": 2650 },
|
||||
"acceleration_print": { "default_value": 2650 },
|
||||
"machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." },
|
||||
"machine_end_gcode": { "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG1 X0 Y200 F3600 ;move extruder out of the way by moving the baseplate to the front for easier access to printed object\nM84 ;steppers off" }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ Menu
|
|||
{
|
||||
text: model.name
|
||||
checkable: model.available
|
||||
enabled: model.available
|
||||
checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
|
||||
exclusiveGroup: group
|
||||
onTriggered: Cura.MachineManager.setQualityChangesGroup(model.quality_changes_group)
|
||||
|
|
|
@ -47,6 +47,19 @@ TabView
|
|||
return Math.round(diameter);
|
||||
}
|
||||
|
||||
// This trick makes sure to make all fields lose focus so their onEditingFinished will be triggered
|
||||
// and modified values will be saved. This can happen when a user changes a value and then closes the
|
||||
// dialog directly.
|
||||
//
|
||||
// Please note that somehow this callback is ONLY triggered when visible is false.
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (!visible)
|
||||
{
|
||||
base.focus = false;
|
||||
}
|
||||
}
|
||||
|
||||
Tab
|
||||
{
|
||||
title: catalog.i18nc("@title", "Information")
|
||||
|
|
|
@ -52,6 +52,24 @@ Item
|
|||
return base.currentItem.root_material_id == root_material_id;
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
// Select the activated material when this page shows up
|
||||
const extruder_position = Cura.ExtruderManager.activeExtruderIndex;
|
||||
const active_root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position];
|
||||
var itemIndex = -1;
|
||||
for (var i = 0; i < materialsModel.rowCount(); ++i)
|
||||
{
|
||||
var item = materialsModel.getItem(i);
|
||||
if (item.root_material_id == active_root_material_id)
|
||||
{
|
||||
itemIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
materialListView.currentIndex = itemIndex;
|
||||
}
|
||||
|
||||
Row // Button Row
|
||||
{
|
||||
id: buttonRow
|
||||
|
|
|
@ -14,6 +14,15 @@ Tab
|
|||
property string extruderPosition: ""
|
||||
property var qualityItem: null
|
||||
|
||||
property bool isQualityItemCurrentlyActivated:
|
||||
{
|
||||
if (qualityItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName;
|
||||
}
|
||||
|
||||
TableView
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
@ -36,8 +45,8 @@ Tab
|
|||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
text: (styleData.value.substr(0,1) == "=") ? catalog.i18nc("@info:status", "Calculated") : styleData.value
|
||||
font.strikeout: styleData.column == 1 && setting.user_value != "" && qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName
|
||||
font.italic: setting.profile_value_source == "quality_changes" || (setting.user_value != "" && qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName)
|
||||
font.strikeout: styleData.column == 1 && setting.user_value != "" && base.isQualityItemCurrentlyActivated
|
||||
font.italic: setting.profile_value_source == "quality_changes" || (setting.user_value != "" && base.isQualityItemCurrentlyActivated)
|
||||
opacity: font.strikeout ? 0.5 : 1
|
||||
color: styleData.textColor
|
||||
elide: Text.ElideRight
|
||||
|
@ -63,7 +72,7 @@ Tab
|
|||
{
|
||||
role: "user_value"
|
||||
title: catalog.i18nc("@title:column", "Current");
|
||||
visible: qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName
|
||||
visible: base.isQualityItemCurrentlyActivated
|
||||
width: (parent.width * 0.18) | 0
|
||||
delegate: itemDelegate
|
||||
}
|
||||
|
@ -86,7 +95,7 @@ Tab
|
|||
{
|
||||
id: qualitySettings
|
||||
selectedPosition: base.extruderPosition
|
||||
selectedQualityItem: base.qualityItem
|
||||
selectedQualityItem: base.qualityItem == null ? {} : base.qualityItem
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
|
|
@ -36,13 +36,15 @@ Item
|
|||
text: catalog.i18nc("@title:tab", "Profiles")
|
||||
}
|
||||
|
||||
property var hasCurrentItem: qualityListView.currentItem != null
|
||||
property var hasCurrentItem: base.currentItem != null
|
||||
|
||||
property var currentItem: {
|
||||
var current_index = qualityListView.currentIndex;
|
||||
return qualitiesModel.getItem(current_index);
|
||||
return (current_index == -1) ? null : qualitiesModel.getItem(current_index);
|
||||
}
|
||||
|
||||
property var currentItemName: hasCurrentItem ? base.currentItem.name : ""
|
||||
|
||||
property var isCurrentItemActivated: {
|
||||
if (!base.currentItem) {
|
||||
return false;
|
||||
|
@ -235,7 +237,7 @@ Item
|
|||
|
||||
icon: StandardIcon.Question;
|
||||
title: catalog.i18nc("@title:window", "Confirm Remove")
|
||||
text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItem.name)
|
||||
text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
|
||||
standardButtons: StandardButton.Yes | StandardButton.No
|
||||
modality: Qt.ApplicationModal
|
||||
|
||||
|
@ -437,6 +439,7 @@ Item
|
|||
Item
|
||||
{
|
||||
anchors.fill: parent
|
||||
visible: base.currentItem != null
|
||||
|
||||
Item // Profile title Label
|
||||
{
|
||||
|
@ -446,7 +449,7 @@ Item
|
|||
height: childrenRect.height
|
||||
|
||||
Label {
|
||||
text: base.currentItem.name
|
||||
text: base.currentItemName
|
||||
font: UM.Theme.getFont("large")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ Column
|
|||
{
|
||||
label: catalog.i18nc("@label", "Printing Time")
|
||||
value: activePrintJob != null ? getPrettyTime(activePrintJob.timeTotal) : ""
|
||||
width:base.width
|
||||
width: base.width
|
||||
visible: activePrinter != null
|
||||
}
|
||||
|
||||
|
|
|
@ -64,11 +64,11 @@ Rectangle
|
|||
|
||||
function getPrettyTime(time)
|
||||
{
|
||||
var hours = Math.round(time / 3600)
|
||||
var hours = Math.floor(time / 3600)
|
||||
time -= hours * 3600
|
||||
var minutes = Math.round(time / 60);
|
||||
var minutes = Math.floor(time / 60);
|
||||
time -= minutes * 60
|
||||
var seconds = Math.round(time);
|
||||
var seconds = Math.floor(time);
|
||||
|
||||
var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2);
|
||||
return finalTime;
|
||||
|
|
|
@ -103,6 +103,7 @@ material_print_temperature_layer_0
|
|||
material_initial_print_temperature
|
||||
material_final_print_temperature
|
||||
material_extrusion_cool_down_speed
|
||||
default_material_bed_temperature
|
||||
material_bed_temperature
|
||||
material_bed_temperature_layer_0
|
||||
material_diameter
|
||||
|
|
4
run_in_docker.sh
Normal file
4
run_in_docker.sh
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
Xvfb :1 -screen 0 1280x800x16 &
|
||||
export DISPLAY=:1.0
|
||||
python3 cura_app.py --headless
|
Loading…
Add table
Add a link
Reference in a new issue