mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 06:57:28 -06:00
Merge branch 'master' into simple_convex_hull
Conflicts: cura/BuildVolume.py cura/ConvexHullDecorator.py cura/ConvexHullJob.py cura/CuraApplication.py
This commit is contained in:
commit
fd42a43270
215 changed files with 35977 additions and 5826 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -7,6 +7,7 @@ docs/html
|
|||
*.log
|
||||
resources/i18n/en
|
||||
resources/i18n/x-test
|
||||
resources/firmware
|
||||
|
||||
# Editors and IDEs.
|
||||
*kdev*
|
||||
|
@ -15,3 +16,11 @@ resources/i18n/x-test
|
|||
*~
|
||||
*.qm
|
||||
.idea
|
||||
|
||||
# Eclipse+PyDev
|
||||
.project
|
||||
.pydevproject
|
||||
.settings
|
||||
|
||||
# Debian packaging
|
||||
debian*
|
||||
|
|
|
@ -7,6 +7,7 @@ include(GNUInstallDirs)
|
|||
set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository")
|
||||
|
||||
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
||||
# Macro needed to list all sub-directory of a directory.
|
||||
|
|
|
@ -13,7 +13,10 @@ Use [this](https://github.com/Ultimaker/Uranium/wiki/Bug-Reporting-Template) tem
|
|||
For crashes and similar issues, please attach the following information:
|
||||
|
||||
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
|
||||
* The Cura GUI log file, located at (Windows) $User/AppData/Local/cura/cura.log, (OSX) $User/.cura/cura.log, (Ubuntu) $USER/.local/share/cura
|
||||
* The Cura GUI log file, located at
|
||||
* $User/AppData/Local/cura/cura.log (Windows)
|
||||
* $User/Library/Application Support/cura (OSX)
|
||||
* $USER/.local/share/cura (Ubuntu/Linux)
|
||||
* The Cura Engine log, using Help -> Show Engine Log
|
||||
|
||||
Dependencies
|
||||
|
|
|
@ -38,13 +38,9 @@ class BuildVolume(SceneNode):
|
|||
self.setCalculateBoundingBox(False)
|
||||
self._volume_aabb = None
|
||||
|
||||
self._active_profile = None
|
||||
self._active_instance = None
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveInstanceChanged)
|
||||
self._onActiveInstanceChanged()
|
||||
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
self._onActiveProfileChanged()
|
||||
self._active_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
|
||||
def setWidth(self, width):
|
||||
if width: self._width = width
|
||||
|
@ -77,7 +73,7 @@ class BuildVolume(SceneNode):
|
|||
|
||||
## Recalculates the build volume & disallowed areas.
|
||||
def rebuild(self):
|
||||
if self._width == 0 or self._height == 0 or self._depth == 0:
|
||||
if not self._width or not self._height or not self._depth:
|
||||
return
|
||||
|
||||
min_w = -self._width / 2
|
||||
|
@ -149,9 +145,9 @@ class BuildVolume(SceneNode):
|
|||
|
||||
skirt_size = 0.0
|
||||
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
skirt_size = self._getSkirtSize(profile)
|
||||
container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if container_stack:
|
||||
skirt_size = self._getSkirtSize(container_stack)
|
||||
|
||||
# As this works better for UM machines, we only add the disallowed_area_size for the z direction.
|
||||
# This is probably wrong in all other cases. TODO!
|
||||
|
@ -166,52 +162,48 @@ class BuildVolume(SceneNode):
|
|||
def getBoundingBox(self):
|
||||
return self._volume_aabb
|
||||
|
||||
def _onActiveInstanceChanged(self):
|
||||
self._active_instance = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
|
||||
if self._active_instance:
|
||||
self._width = self._active_instance.getMachineSettingValue("machine_width")
|
||||
if Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("print_sequence") == "one_at_a_time":
|
||||
self._height = Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("gantry_height")
|
||||
self._active_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
|
||||
|
||||
self._width = self._active_container_stack.getProperty("machine_width", "value")
|
||||
if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
self._height = self._active_container_stack.getProperty("gantry_height", "value")
|
||||
else:
|
||||
self._height = self._active_instance.getMachineSettingValue("machine_height")
|
||||
self._depth = self._active_instance.getMachineSettingValue("machine_depth")
|
||||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
self._depth = self._active_container_stack.getProperty("machine_depth", "value")
|
||||
|
||||
self._updateDisallowedAreas()
|
||||
|
||||
self.rebuild()
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._active_profile:
|
||||
self._active_profile.settingValueChanged.disconnect(self._onSettingValueChanged)
|
||||
def _onSettingPropertyChanged(self, setting_key, property_name):
|
||||
if property_name != "value":
|
||||
return
|
||||
|
||||
self._active_profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if self._active_profile:
|
||||
self._active_profile.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
self._updateDisallowedAreas()
|
||||
self.rebuild()
|
||||
|
||||
def _onSettingValueChanged(self, setting_key):
|
||||
if setting_key == "print_sequence":
|
||||
if Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("print_sequence") == "one_at_a_time":
|
||||
self._height = Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("gantry_height")
|
||||
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
self._height = self._active_container_stack.getProperty("gantry_height", "value")
|
||||
else:
|
||||
self._height = self._active_instance.getMachineSettingValue("machine_depth")
|
||||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
self.rebuild()
|
||||
if setting_key in self._skirt_settings:
|
||||
self._updateDisallowedAreas()
|
||||
self.rebuild()
|
||||
|
||||
def _updateDisallowedAreas(self):
|
||||
if not self._active_instance or not self._active_profile:
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
|
||||
disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas")
|
||||
disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value")
|
||||
areas = []
|
||||
|
||||
skirt_size = 0.0
|
||||
if self._active_profile:
|
||||
skirt_size = self._getSkirtSize(self._active_profile)
|
||||
skirt_size = self._getSkirtSize(self._active_container_stack)
|
||||
|
||||
if disallowed_areas:
|
||||
# Extend every area already in the disallowed_areas with the skirt size.
|
||||
|
@ -232,8 +224,8 @@ class BuildVolume(SceneNode):
|
|||
|
||||
# Add the skirt areas around the borders of the build plate.
|
||||
if skirt_size > 0:
|
||||
half_machine_width = self._active_instance.getMachineSettingValue("machine_width") / 2
|
||||
half_machine_depth = self._active_instance.getMachineSettingValue("machine_depth") / 2
|
||||
half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
|
||||
half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2
|
||||
|
||||
areas.append(Polygon(numpy.array([
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
|
@ -266,24 +258,24 @@ class BuildVolume(SceneNode):
|
|||
self._disallowed_areas = areas
|
||||
|
||||
## Convenience function to calculate the size of the bed adhesion.
|
||||
def _getSkirtSize(self, profile):
|
||||
def _getSkirtSize(self, container_stack):
|
||||
skirt_size = 0.0
|
||||
|
||||
adhesion_type = profile.getSettingValue("adhesion_type")
|
||||
adhesion_type = container_stack.getProperty("adhesion_type", "value")
|
||||
if adhesion_type == "skirt":
|
||||
skirt_distance = profile.getSettingValue("skirt_gap")
|
||||
skirt_line_count = profile.getSettingValue("skirt_line_count")
|
||||
skirt_size = skirt_distance + (skirt_line_count * profile.getSettingValue("skirt_line_width"))
|
||||
skirt_distance = container_stack.getProperty("skirt_gap", "value")
|
||||
skirt_line_count = container_stack.getProperty("skirt_line_count", "value")
|
||||
skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_line_width", "value"))
|
||||
elif adhesion_type == "brim":
|
||||
skirt_size = profile.getSettingValue("brim_line_count") * profile.getSettingValue("skirt_line_width")
|
||||
skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_line_width", "value")
|
||||
elif adhesion_type == "raft":
|
||||
skirt_size = profile.getSettingValue("raft_margin")
|
||||
skirt_size = container_stack.getProperty("raft_margin", "value")
|
||||
|
||||
if profile.getSettingValue("draft_shield_enabled"):
|
||||
skirt_size += profile.getSettingValue("draft_shield_dist")
|
||||
if container_stack.getProperty("draft_shield_enabled", "value"):
|
||||
skirt_size += container_stack.getProperty("draft_shield_dist", "value")
|
||||
|
||||
if profile.getSettingValue("xy_offset"):
|
||||
skirt_size += profile.getSettingValue("xy_offset")
|
||||
if container_stack.getProperty("xy_offset", "value"):
|
||||
skirt_size += container_stack.getProperty("xy_offset", "value")
|
||||
|
||||
return skirt_size
|
||||
|
||||
|
|
93
cura/ContainerSettingsModel.py
Normal file
93
cura/ContainerSettingsModel.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
||||
class ContainerSettingsModel(ListModel):
|
||||
LabelRole = Qt.UserRole + 1
|
||||
CategoryRole = Qt.UserRole + 2
|
||||
UnitRole = Qt.UserRole + 3
|
||||
ValuesRole = Qt.UserRole + 4
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self.addRoleName(self.LabelRole, "label")
|
||||
self.addRoleName(self.CategoryRole, "category")
|
||||
self.addRoleName(self.UnitRole, "unit")
|
||||
self.addRoleName(self.ValuesRole, "values")
|
||||
|
||||
self._container_ids = []
|
||||
self._containers = []
|
||||
|
||||
def _onPropertyChanged(self, key, property_name):
|
||||
if property_name == "value":
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
self.clear()
|
||||
|
||||
if len(self._container_ids) == 0:
|
||||
return
|
||||
|
||||
keys = []
|
||||
for container in self._containers:
|
||||
keys = keys + list(container.getAllKeys())
|
||||
|
||||
keys = list(set(keys)) # remove duplicate keys
|
||||
keys.sort()
|
||||
|
||||
for key in keys:
|
||||
definition = None
|
||||
category = None
|
||||
values = []
|
||||
for container in self._containers:
|
||||
instance = container.getInstance(key)
|
||||
if instance:
|
||||
definition = instance.definition
|
||||
|
||||
# Traverse up to find the category
|
||||
category = definition
|
||||
while category.type != "category":
|
||||
category = category.parent
|
||||
|
||||
value = container.getProperty(key, "value")
|
||||
if type(value) == SettingFunction:
|
||||
values.append("=\u0192")
|
||||
else:
|
||||
values.append(container.getProperty(key, "value"))
|
||||
else:
|
||||
values.append("")
|
||||
|
||||
self.appendItem({
|
||||
"key": key,
|
||||
"values": values,
|
||||
"label": definition.label,
|
||||
"unit": definition.unit,
|
||||
"category": category.label
|
||||
})
|
||||
|
||||
## Set the ids of the containers which have the settings this model should list.
|
||||
# Also makes sure the model updates when the containers have property changes
|
||||
def setContainers(self, container_ids):
|
||||
for container in self._containers:
|
||||
container.propertyChanged.disconnect(self._onPropertyChanged)
|
||||
|
||||
self._container_ids = container_ids
|
||||
self._containers = []
|
||||
|
||||
for container_id in self._container_ids:
|
||||
containers = ContainerRegistry.getInstance().findContainers(id = container_id)
|
||||
if containers:
|
||||
containers[0].propertyChanged.connect(self._onPropertyChanged)
|
||||
self._containers.append(containers[0])
|
||||
|
||||
self._update()
|
||||
|
||||
containersChanged = pyqtSignal()
|
||||
@pyqtProperty("QVariantList", fset = setContainers, notify = containersChanged)
|
||||
def containers(self):
|
||||
return self.container_ids
|
|
@ -15,10 +15,9 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
self._convex_hull_node = None
|
||||
self._init2DConvexHullCache()
|
||||
|
||||
self._profile = None
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
||||
self._onActiveProfileChanged()
|
||||
self._global_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
## Force that a new (empty) object is created upon copy.
|
||||
def __deepcopy__(self, memo):
|
||||
|
@ -26,25 +25,31 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
## Get the unmodified 2D projected convex hull of the node
|
||||
def getConvexHull(self):
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
hull = self._compute2DConvexHull()
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"), numpy.float32)))
|
||||
if self._global_stack and self._node:
|
||||
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon"), numpy.float32)))
|
||||
return hull
|
||||
|
||||
## Get the convex hull of the node with the full head size
|
||||
def getConvexHullHeadFull(self):
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
return self._compute2DConvexHeadFull()
|
||||
|
||||
## Get convex hull of the object + head size
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is area with intersection of mirrored head
|
||||
def getConvexHullHead(self):
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration(
|
||||
"isGroup"):
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
if self._global_stack:
|
||||
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
return self._compute2DConvexHeadMin()
|
||||
return None
|
||||
|
||||
|
@ -52,15 +57,19 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is the area without the head.
|
||||
def getConvexHullBoundary(self):
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration(
|
||||
"isGroup"):
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
if self._global_stack:
|
||||
if self._global_stack("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
# Printing one at a time and it's not an object in a group
|
||||
return self._compute2DConvexHull()
|
||||
return None
|
||||
|
||||
def recomputeConvexHull(self):
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
convex_hull = self.getConvexHull()
|
||||
if self._convex_hull_node:
|
||||
if self._convex_hull_node.getHull() == convex_hull:
|
||||
|
@ -70,23 +79,9 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
Application.getInstance().getController().getScene().getRoot())
|
||||
self._convex_hull_node = hull_node
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._onSettingValueChanged)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
|
||||
def _onActiveMachineInstanceChanged(self):
|
||||
if self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def _onSettingValueChanged(self, setting):
|
||||
if setting == "print_sequence":
|
||||
self.recomputeConvexHull()
|
||||
def _onSettingValueChanged(self, key, property_name):
|
||||
if key == "print_sequence" and property_name == "value":
|
||||
self._onChanged()
|
||||
|
||||
def _init2DConvexHullCache(self):
|
||||
# Cache for the group code path in _compute2DConvexHull()
|
||||
|
@ -178,8 +173,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
return rounded_hull
|
||||
|
||||
def _getHeadAndFans(self):
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
return Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32))
|
||||
return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon"), numpy.float32))
|
||||
|
||||
def _compute2DConvexHeadFull(self):
|
||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||
|
@ -195,3 +189,19 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
def _roundHull(self, convex_hull):
|
||||
return convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||
|
||||
def _onChanged(self, *args):
|
||||
self.recomputeConvexHull()
|
||||
|
||||
def _onGlobalStackChanged(self):
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged)
|
||||
self._global_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
||||
self._global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self._onSettingValueChanged)
|
||||
self._global_stack.containersChanged.connect(self._onChanged)
|
||||
|
||||
self._onChanged()
|
||||
|
|
|
@ -5,17 +5,33 @@ import webbrowser
|
|||
|
||||
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication
|
||||
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
# List of exceptions that should be considered "fatal" and abort the program.
|
||||
# These are primarily some exception types that we simply cannot really recover from
|
||||
# (MemoryError and SystemError) and exceptions that indicate grave errors in the
|
||||
# code that cause the Python interpreter to fail (SyntaxError, ImportError).
|
||||
fatal_exception_types = [
|
||||
MemoryError,
|
||||
SyntaxError,
|
||||
ImportError,
|
||||
SystemError,
|
||||
]
|
||||
|
||||
def show(exception_type, value, tb):
|
||||
debug_mode = False
|
||||
if QCoreApplication.instance():
|
||||
debug_mode = QCoreApplication.instance().getCommandLineOption("debug-mode", False)
|
||||
|
||||
traceback.print_exception(exception_type, value, tb)
|
||||
Logger.log("c", "An uncaught exception has occurred!")
|
||||
for line in traceback.format_exception(exception_type, value, tb):
|
||||
for part in line.rstrip("\n").split("\n"):
|
||||
Logger.log("c", part)
|
||||
|
||||
if not debug_mode:
|
||||
if not debug_mode and exception_type not in fatal_exception_types:
|
||||
return
|
||||
|
||||
application = QCoreApplication.instance()
|
||||
|
@ -29,7 +45,7 @@ def show(exception_type, value, tb):
|
|||
|
||||
label = QLabel(dialog)
|
||||
layout.addWidget(label)
|
||||
label.setText(catalog.i18nc("@label", "<p>An uncaught exception has occurred!</p><p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>"))
|
||||
label.setText(catalog.i18nc("@label", "<p>A fatal exception has occurred that we could not recover from!</p><p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>"))
|
||||
|
||||
textarea = QTextEdit(dialog)
|
||||
layout.addWidget(textarea)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from UM.Qt.QtApplication import QtApplication
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Camera import Camera
|
||||
from UM.Scene.Platform import Platform
|
||||
from UM.Scene.Platform import Platform as Scene_Platform
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.Quaternion import Quaternion
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
|
@ -14,18 +14,26 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Mesh.ReadMeshJob import ReadMeshJob
|
||||
from UM.Logger import Logger
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Platform import Platform
|
||||
from UM.JobQueue import JobQueue
|
||||
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
import UM.Settings.Validator
|
||||
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
from UM.Operations.GroupedOperation import GroupedOperation
|
||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||
from cura.SetParentOperation import SetParentOperation
|
||||
|
||||
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import ExtruderManager
|
||||
from . import ExtrudersModel
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
|
@ -34,20 +42,23 @@ from . import CuraActions
|
|||
from . import MultiMaterialDecorator
|
||||
from . import ZOffsetDecorator
|
||||
from . import CuraSplashScreen
|
||||
from . import MachineManagerModel
|
||||
from . import ContainerSettingsModel
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
import platform
|
||||
import sys
|
||||
import os.path
|
||||
import numpy
|
||||
import copy
|
||||
import urllib
|
||||
numpy.seterr(all="ignore")
|
||||
|
||||
#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
|
||||
if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||
if platform.linux_distribution()[0] in ("Ubuntu", ): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
import ctypes
|
||||
|
@ -55,15 +66,23 @@ if platform.system() == "Linux": # Needed for platform.linux_distribution, which
|
|||
ctypes.CDLL(find_library('GL'), ctypes.RTLD_GLOBAL)
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraVersion
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType
|
||||
except ImportError:
|
||||
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
||||
CuraBuildType = ""
|
||||
|
||||
|
||||
class CuraApplication(QtApplication):
|
||||
class ResourceTypes:
|
||||
QmlFiles = Resources.UserType + 1
|
||||
Firmware = Resources.UserType + 2
|
||||
QualityInstanceContainer = Resources.UserType + 3
|
||||
MaterialInstanceContainer = Resources.UserType + 4
|
||||
VariantInstanceContainer = Resources.UserType + 5
|
||||
UserInstanceContainer = Resources.UserType + 6
|
||||
MachineStack = Resources.UserType + 7
|
||||
ExtruderStack = Resources.UserType + 8
|
||||
|
||||
Q_ENUMS(ResourceTypes)
|
||||
|
||||
def __init__(self):
|
||||
|
@ -73,7 +92,14 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._open_file_queue = [] # Files to open when plug-ins are loaded.
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion)
|
||||
# Need to do this before ContainerRegistry tries to load the machines
|
||||
SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator)
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType)
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
||||
|
@ -95,31 +121,109 @@ class CuraApplication(QtApplication):
|
|||
self._i18n_catalog = None
|
||||
self._previous_active_tool = None
|
||||
self._platform_activity = False
|
||||
self._scene_boundingbox = AxisAlignedBox.Null
|
||||
self._scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
self._job_name = None
|
||||
self._center_after_select = False
|
||||
self._camera_animation = None
|
||||
self._cura_actions = None
|
||||
self._started = False
|
||||
|
||||
self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged)
|
||||
self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested)
|
||||
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
|
||||
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_machine", "")
|
||||
## Add the 4 types of profiles to storage.
|
||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||
Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
|
||||
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
|
||||
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
|
||||
|
||||
# Add empty variant, material and quality containers.
|
||||
# Since they are empty, they should never be serialized and instead just programmatically created.
|
||||
# We need them to simplify the switching between materials.
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container._id = "empty_variant"
|
||||
empty_variant_container.addMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container._id = "empty_material"
|
||||
empty_material_container.addMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container._id = "empty_quality"
|
||||
empty_quality_container.addMetaDataEntry("type", "quality")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
ContainerRegistry.getInstance().load()
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_mode", "simple")
|
||||
Preferences.getInstance().addPreference("cura/recent_files", "")
|
||||
Preferences.getInstance().addPreference("cura/categories_expanded", "")
|
||||
Preferences.getInstance().addPreference("cura/jobname_prefix", True)
|
||||
Preferences.getInstance().addPreference("view/center_on_select", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True)
|
||||
Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
|
||||
|
||||
Preferences.getInstance().setDefault("general/visible_settings", """
|
||||
machine_settings
|
||||
resolution
|
||||
layer_height
|
||||
shell
|
||||
wall_thickness
|
||||
top_bottom_thickness
|
||||
infill
|
||||
infill_sparse_density
|
||||
material
|
||||
material_print_temperature
|
||||
material_bed_temperature
|
||||
material_diameter
|
||||
material_flow
|
||||
retraction_enable
|
||||
speed
|
||||
speed_print
|
||||
speed_travel
|
||||
acceleration_print
|
||||
acceleration_travel
|
||||
jerk_print
|
||||
jerk_travel
|
||||
travel
|
||||
cooling
|
||||
cool_fan_enabled
|
||||
support
|
||||
support_enable
|
||||
support_type
|
||||
support_roof_density
|
||||
platform_adhesion
|
||||
adhesion_type
|
||||
brim_width
|
||||
raft_airgap
|
||||
layer_0_z_overlap
|
||||
raft_surface_layers
|
||||
meshfix
|
||||
blackmagic
|
||||
print_sequence
|
||||
dual
|
||||
experimental
|
||||
""".replace("\n", ";").replace(" ", ""))
|
||||
|
||||
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
|
||||
|
||||
self.applicationShuttingDown.connect(self.saveSettings)
|
||||
|
||||
self._recent_files = []
|
||||
files = Preferences.getInstance().getValue("cura/recent_files").split(";")
|
||||
for f in files:
|
||||
|
@ -128,6 +232,65 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._recent_files.append(QUrl.fromLocalFile(f))
|
||||
|
||||
## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||
#
|
||||
# Note that the AutoSave plugin also calls this method.
|
||||
def saveSettings(self):
|
||||
if not self._started: # Do not do saving during application start
|
||||
return
|
||||
|
||||
for instance in ContainerRegistry.getInstance().findInstanceContainers():
|
||||
if not instance.isDirty():
|
||||
continue
|
||||
|
||||
try:
|
||||
data = instance.serialize()
|
||||
except NotImplementedError:
|
||||
continue
|
||||
except Exception:
|
||||
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
|
||||
continue
|
||||
|
||||
file_name = urllib.parse.quote_plus(instance.getId()) + ".inst.cfg"
|
||||
instance_type = instance.getMetaDataEntry("type")
|
||||
path = None
|
||||
if instance_type == "material":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name)
|
||||
elif instance_type == "quality":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name)
|
||||
elif instance_type == "user":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name)
|
||||
elif instance_type == "variant":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
|
||||
|
||||
if path:
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f:
|
||||
f.write(data)
|
||||
|
||||
for stack in ContainerRegistry.getInstance().findContainerStacks():
|
||||
if not stack.isDirty():
|
||||
continue
|
||||
|
||||
try:
|
||||
data = stack.serialize()
|
||||
except NotImplementedError:
|
||||
continue
|
||||
except Exception:
|
||||
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
|
||||
continue
|
||||
|
||||
file_name = urllib.parse.quote_plus(stack.getId()) + ".stack.cfg"
|
||||
stack_type = stack.getMetaDataEntry("type", None)
|
||||
path = None
|
||||
if not stack_type or stack_type == "machine":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
|
||||
elif stack_type == "extruder_train":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
|
||||
if path:
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
@pyqtSlot(result = QUrl)
|
||||
def getDefaultPath(self):
|
||||
return QUrl.fromLocalFile(os.path.expanduser("~/"))
|
||||
|
@ -135,6 +298,8 @@ class CuraApplication(QtApplication):
|
|||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistery
|
||||
def _loadPlugins(self):
|
||||
self._plugin_registry.addType("profile_reader", self._addProfileReader)
|
||||
self._plugin_registry.addType("profile_writer", self._addProfileWriter)
|
||||
self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura"))
|
||||
if not hasattr(sys, "frozen"):
|
||||
self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))
|
||||
|
@ -176,7 +341,7 @@ class CuraApplication(QtApplication):
|
|||
Selection.selectionChanged.connect(self.onSelectionChanged)
|
||||
|
||||
root = controller.getScene().getRoot()
|
||||
self._platform = Platform(root)
|
||||
self._platform = Scene_Platform(root)
|
||||
|
||||
self._volume = BuildVolume.BuildVolume(root)
|
||||
|
||||
|
@ -197,7 +362,13 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
||||
|
||||
# Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||
ExtruderManager.ExtruderManager.getInstance()
|
||||
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
|
||||
MachineManagerModel.createMachineManagerModel)
|
||||
|
||||
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
||||
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))
|
||||
self.initializeEngine()
|
||||
|
||||
if self._engine.rootObjects:
|
||||
|
@ -208,6 +379,8 @@ class CuraApplication(QtApplication):
|
|||
for file_name in self._open_file_queue: #Open all the files that were queued up while plug-ins were loading.
|
||||
self._openFile(file_name)
|
||||
|
||||
self._started = True
|
||||
|
||||
self.exec_()
|
||||
|
||||
## Handle Qt events
|
||||
|
@ -224,6 +397,9 @@ class CuraApplication(QtApplication):
|
|||
def getPrintInformation(self):
|
||||
return self._print_information
|
||||
|
||||
## Registers objects for the QML engine to use.
|
||||
#
|
||||
# \param engine The QML engine.
|
||||
def registerObjects(self, engine):
|
||||
engine.rootContext().setContextProperty("Printer", self)
|
||||
self._print_information = PrintInformation.PrintInformation()
|
||||
|
@ -233,6 +409,21 @@ class CuraApplication(QtApplication):
|
|||
|
||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||
|
||||
qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
||||
|
||||
qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
|
||||
|
||||
qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions")
|
||||
|
||||
engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance())
|
||||
|
||||
for path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.QmlFiles):
|
||||
type_name = os.path.splitext(os.path.basename(path))[0]
|
||||
if type_name in ("Cura", "Actions"):
|
||||
continue
|
||||
|
||||
qmlRegisterType(QUrl.fromLocalFile(path), "Cura", 1, 0, type_name)
|
||||
|
||||
def onSelectionChanged(self):
|
||||
if Selection.hasSelection():
|
||||
if not self.getController().getActiveTool():
|
||||
|
@ -267,46 +458,33 @@ class CuraApplication(QtApplication):
|
|||
|
||||
@pyqtProperty(str, notify = sceneBoundingBoxChanged)
|
||||
def getSceneBoundingBoxString(self):
|
||||
return self._i18n_catalog.i18nc("@info", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_boundingbox.width.item(), 'depth': self._scene_boundingbox.depth.item(), 'height' : self._scene_boundingbox.height.item()}
|
||||
return self._i18n_catalog.i18nc("@info", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}
|
||||
|
||||
def updatePlatformActivity(self, node = None):
|
||||
count = 0
|
||||
scene_boundingbox = None
|
||||
scene_bounding_box = None
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
continue
|
||||
|
||||
count += 1
|
||||
if not scene_boundingbox:
|
||||
scene_boundingbox = node.getBoundingBox()
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = node.getBoundingBox()
|
||||
else:
|
||||
other_bb = node.getBoundingBox()
|
||||
if other_bb is not None:
|
||||
scene_boundingbox = scene_boundingbox + node.getBoundingBox()
|
||||
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||
|
||||
if not scene_boundingbox:
|
||||
scene_boundingbox = AxisAlignedBox.Null
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
if repr(self._scene_boundingbox) != repr(scene_boundingbox):
|
||||
self._scene_boundingbox = scene_boundingbox
|
||||
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
|
||||
self._scene_bounding_box = scene_bounding_box
|
||||
self.sceneBoundingBoxChanged.emit()
|
||||
|
||||
self._platform_activity = True if count > 0 else False
|
||||
self.activityChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
name = os.path.splitext(name)[0] #when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its extension. This cuts the extension off if nescessary.
|
||||
if self._job_name != name:
|
||||
self._job_name = name
|
||||
self.jobNameChanged.emit()
|
||||
|
||||
jobNameChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(str, notify = jobNameChanged)
|
||||
def jobName(self):
|
||||
return self._job_name
|
||||
|
||||
# Remove all selected objects from the scene.
|
||||
@pyqtSlot()
|
||||
def deleteSelection(self):
|
||||
|
@ -331,7 +509,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
|
@ -350,7 +528,7 @@ class CuraApplication(QtApplication):
|
|||
def multiplyObject(self, object_id, count):
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
|
@ -372,7 +550,7 @@ class CuraApplication(QtApplication):
|
|||
@pyqtSlot("quint64")
|
||||
def centerObject(self, object_id):
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if not node:
|
||||
|
@ -385,7 +563,7 @@ class CuraApplication(QtApplication):
|
|||
op = SetTransformOperation(node, Vector())
|
||||
op.push()
|
||||
|
||||
## Delete all mesh data on the scene.
|
||||
## Delete all nodes containing mesh data in the scene.
|
||||
@pyqtSlot()
|
||||
def deleteAll(self):
|
||||
if not self.getController().getToolsEnabled():
|
||||
|
@ -396,9 +574,9 @@ class CuraApplication(QtApplication):
|
|||
if type(node) is not SceneNode:
|
||||
continue
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
continue #Node that doesnt have a mesh and is not a group.
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
nodes.append(node)
|
||||
if nodes:
|
||||
op = GroupedOperation()
|
||||
|
@ -416,9 +594,9 @@ class CuraApplication(QtApplication):
|
|||
if type(node) is not SceneNode:
|
||||
continue
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
continue #Node that doesnt have a mesh and is not a group.
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
|
||||
nodes.append(node)
|
||||
|
||||
|
@ -438,9 +616,9 @@ class CuraApplication(QtApplication):
|
|||
if type(node) is not SceneNode:
|
||||
continue
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
continue #Node that doesnt have a mesh and is not a group.
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
nodes.append(node)
|
||||
|
||||
if nodes:
|
||||
|
@ -479,7 +657,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
## Get logging data of the backend engine
|
||||
# \returns \type{string} Logging data
|
||||
@pyqtSlot(result=str)
|
||||
@pyqtSlot(result = str)
|
||||
def getEngineLog(self):
|
||||
log = ""
|
||||
|
||||
|
@ -509,21 +687,6 @@ class CuraApplication(QtApplication):
|
|||
def expandedCategories(self):
|
||||
return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
|
||||
|
||||
@pyqtSlot(str, result = "QVariant")
|
||||
def getSettingValue(self, key):
|
||||
if not self.getMachineManager().getWorkingProfile():
|
||||
return None
|
||||
return self.getMachineManager().getWorkingProfile().getSettingValue(key)
|
||||
#return self.getActiveMachine().getSettingValueByKey(key)
|
||||
|
||||
## Change setting by key value pair
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setSettingValue(self, key, value):
|
||||
if not self.getMachineManager().getWorkingProfile():
|
||||
return
|
||||
|
||||
self.getMachineManager().getWorkingProfile().setSettingValue(key, value)
|
||||
|
||||
@pyqtSlot()
|
||||
def mergeSelected(self):
|
||||
self.groupSelected()
|
||||
|
@ -542,9 +705,10 @@ class CuraApplication(QtApplication):
|
|||
|
||||
# Use the previously found center of the group bounding box as the new location of the group
|
||||
group_node.setPosition(group_node.getBoundingBox().center)
|
||||
|
||||
|
||||
@pyqtSlot()
|
||||
def groupSelected(self):
|
||||
# Create a group-node
|
||||
group_node = SceneNode()
|
||||
group_decorator = GroupDecorator()
|
||||
group_node.addDecorator(group_decorator)
|
||||
|
@ -554,40 +718,34 @@ class CuraApplication(QtApplication):
|
|||
group_node.setPosition(center)
|
||||
group_node.setCenterPosition(center)
|
||||
|
||||
for node in Selection.getAllSelectedObjects():
|
||||
world = node.getWorldPosition()
|
||||
node.setParent(group_node)
|
||||
node.setPosition(world - center)
|
||||
# Move selected nodes into the group-node
|
||||
Selection.applyOperation(SetParentOperation, group_node)
|
||||
|
||||
# Deselect individual nodes and select the group-node instead
|
||||
for node in group_node.getChildren():
|
||||
Selection.remove(node)
|
||||
|
||||
Selection.add(group_node)
|
||||
|
||||
@pyqtSlot()
|
||||
def ungroupSelected(self):
|
||||
ungrouped_nodes = []
|
||||
selected_objects = Selection.getAllSelectedObjects()[:] #clone the list
|
||||
selected_objects = Selection.getAllSelectedObjects().copy()
|
||||
for node in selected_objects:
|
||||
if node.callDecoration("isGroup" ):
|
||||
children_to_move = []
|
||||
for child in node.getChildren():
|
||||
if type(child) is SceneNode:
|
||||
children_to_move.append(child)
|
||||
if node.callDecoration("isGroup"):
|
||||
op = GroupedOperation()
|
||||
|
||||
for child in children_to_move:
|
||||
position = child.getWorldPosition()
|
||||
child.setParent(node.getParent())
|
||||
child.setPosition(position - node.getParent().getWorldPosition())
|
||||
child.scale(node.getScale())
|
||||
child.rotate(node.getOrientation())
|
||||
group_parent = node.getParent()
|
||||
children = node.getChildren().copy()
|
||||
for child in children:
|
||||
# Set the parent of the children to the parent of the group-node
|
||||
op.addOperation(SetParentOperation(child, group_parent))
|
||||
|
||||
# Add all individual nodes to the selection
|
||||
Selection.add(child)
|
||||
child.callDecoration("setConvexHull",None)
|
||||
node.setParent(None)
|
||||
ungrouped_nodes.append(node)
|
||||
for node in ungrouped_nodes:
|
||||
Selection.remove(node)
|
||||
child.callDecoration("setConvexHull", None)
|
||||
|
||||
op.push()
|
||||
# Note: The group removes itself from the scene once all its children have left it,
|
||||
# see GroupDecorator._onChildrenChanged
|
||||
|
||||
def _createSplashScreen(self):
|
||||
return CuraSplashScreen.CuraSplashScreen()
|
||||
|
@ -595,10 +753,12 @@ class CuraApplication(QtApplication):
|
|||
def _onActiveMachineChanged(self):
|
||||
pass
|
||||
|
||||
fileLoaded = pyqtSignal(str)
|
||||
|
||||
def _onFileLoaded(self, job):
|
||||
node = job.getResult()
|
||||
if node != None:
|
||||
self.setJobName(os.path.basename(job.getFileName()))
|
||||
self.fileLoaded.emit(job.getFileName())
|
||||
node.setSelectable(True)
|
||||
node.setName(os.path.basename(job.getFileName()))
|
||||
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
|
||||
|
@ -628,14 +788,15 @@ class CuraApplication(QtApplication):
|
|||
def _reloadMeshFinished(self, job):
|
||||
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
|
||||
job._node.setMeshData(job.getResult().getMeshData())
|
||||
#job.getResult().setParent(self.getController().getScene().getRoot())
|
||||
#job._node.setParent(self.getController().getScene().getRoot())
|
||||
#job._node.meshDataChanged.emit(job._node)
|
||||
|
||||
def _openFile(self, file):
|
||||
job = ReadMeshJob(os.path.abspath(file))
|
||||
job.finished.connect(self._onFileLoaded)
|
||||
job.start()
|
||||
|
||||
def _onAddMachineRequested(self):
|
||||
self.requestAddPrinter.emit()
|
||||
def _addProfileReader(self, profile_reader):
|
||||
# TODO: Add the profile reader to the list of plug-ins that can be used when importing profiles.
|
||||
pass
|
||||
|
||||
def _addProfileWriter(self, profile_writer):
|
||||
pass
|
||||
|
|
209
cura/CuraContainerRegistry.py
Normal file
209
cura/CuraContainerRegistry.py
Normal file
|
@ -0,0 +1,209 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Platform import Platform
|
||||
from UM.PluginRegistry import PluginRegistry #For getting the possible profile writers to write with.
|
||||
from UM.Util import parseBool
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
class CuraContainerRegistry(ContainerRegistry):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
## Create a name that is not empty and unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
# \param current_name \type{} Current name of the container, which may be an acceptable option
|
||||
# \param new_name \type{string} Base name, which may not be unique
|
||||
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
|
||||
# \return \type{string} Name that is unique for the specified type and name/id
|
||||
def createUniqueName(self, container_type, current_name, new_name, fallback_name):
|
||||
new_name = new_name.strip()
|
||||
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
|
||||
if num_check:
|
||||
new_name = num_check.group(1)
|
||||
if new_name == "":
|
||||
new_name = fallback_name
|
||||
|
||||
unique_name = new_name
|
||||
i = 1
|
||||
# In case we are renaming, the current name of the container is also a valid end-result
|
||||
while self._containerExists(container_type, unique_name) and unique_name != current_name:
|
||||
i += 1
|
||||
unique_name = "%s #%d" % (new_name, i)
|
||||
|
||||
return unique_name
|
||||
|
||||
## Check if a container with of a certain type and a certain name or id exists
|
||||
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
# \param container_name \type{string} Name to check
|
||||
def _containerExists(self, container_type, container_name):
|
||||
container_class = ContainerStack if container_type == "machine" else InstanceContainer
|
||||
|
||||
return self.findContainers(container_class, id = container_name, type = container_type) or \
|
||||
self.findContainers(container_class, name = container_name, type = container_type)
|
||||
|
||||
## Exports an profile to a file
|
||||
#
|
||||
# \param instance_id \type{str} the ID of the profile to export.
|
||||
# \param file_name \type{str} the full path and filename to export to.
|
||||
# \param file_type \type{str} the file type with the format "<description> (*.<extension>)"
|
||||
def exportProfile(self, instance_id, file_name, file_type):
|
||||
Logger.log('d', 'exportProfile instance_id: '+str(instance_id))
|
||||
|
||||
# Parse the fileType to deduce what plugin can save the file format.
|
||||
# fileType has the format "<description> (*.<extension>)"
|
||||
split = file_type.rfind(" (*.") # Find where the description ends and the extension starts.
|
||||
if split < 0: # Not found. Invalid format.
|
||||
Logger.log("e", "Invalid file format identifier %s", file_type)
|
||||
return
|
||||
description = file_type[:split]
|
||||
extension = file_type[split + 4:-1] # Leave out the " (*." and ")".
|
||||
if not file_name.endswith("." + extension): # Auto-fill the extension if the user did not provide any.
|
||||
file_name += "." + extension
|
||||
|
||||
# On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
|
||||
if not Platform.isWindows():
|
||||
if os.path.exists(file_name):
|
||||
result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
|
||||
catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
|
||||
if result == QMessageBox.No:
|
||||
return
|
||||
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
|
||||
if not containers:
|
||||
return
|
||||
container = containers[0]
|
||||
|
||||
profile_writer = self._findProfileWriter(extension, description)
|
||||
|
||||
try:
|
||||
success = profile_writer.write(file_name, container)
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
|
||||
m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)), lifetime = 0)
|
||||
m.show()
|
||||
return
|
||||
if not success:
|
||||
Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
|
||||
m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name), lifetime = 0)
|
||||
m.show()
|
||||
return
|
||||
m = Message(catalog.i18nc("@info:status", "Exported profile to <filename>{0}</filename>", file_name))
|
||||
m.show()
|
||||
|
||||
## Gets the plugin object matching the criteria
|
||||
# \param extension
|
||||
# \param description
|
||||
# \return The plugin object matching the given extension and description.
|
||||
def _findProfileWriter(self, extension, description):
|
||||
plugin_registry = PluginRegistry.getInstance()
|
||||
for plugin_id, meta_data in self._getIOPlugins("profile_writer"):
|
||||
for supported_type in meta_data["profile_writer"]: # All file types this plugin can supposedly write.
|
||||
supported_extension = supported_type.get("extension", None)
|
||||
if supported_extension == extension: # This plugin supports a file type with the same extension.
|
||||
supported_description = supported_type.get("description", None)
|
||||
if supported_description == description: # The description is also identical. Assume it's the same file type.
|
||||
return plugin_registry.getPluginObject(plugin_id)
|
||||
return None
|
||||
|
||||
## Imports a profile from a file
|
||||
#
|
||||
# \param file_name \type{str} the full path and filename of the profile to import
|
||||
# \return \type{Dict} dict with a 'status' key containing the string 'ok' or 'error', and a 'message' key
|
||||
# containing a message for the user
|
||||
def importProfile(self, file_name):
|
||||
if not file_name:
|
||||
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}
|
||||
|
||||
plugin_registry = PluginRegistry.getInstance()
|
||||
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
|
||||
profile_reader = plugin_registry.getPluginObject(plugin_id)
|
||||
try:
|
||||
profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
|
||||
except Exception as e:
|
||||
#Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
|
||||
Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
|
||||
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
|
||||
if profile: #Success!
|
||||
profile.setReadOnly(False)
|
||||
|
||||
new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
|
||||
catalog.i18nc("@label", "Custom profile"))
|
||||
profile.setName(new_name)
|
||||
profile._id = new_name
|
||||
|
||||
if self._machineHasOwnQualities():
|
||||
profile.setDefinition(self._activeDefinition())
|
||||
if self._machineHasOwnMaterials():
|
||||
profile.addMetaDataEntry("material", self._activeMaterialId())
|
||||
else:
|
||||
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
|
||||
ContainerRegistry.getInstance().addContainer(profile)
|
||||
|
||||
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
|
||||
|
||||
#If it hasn't returned by now, none of the plugins loaded the profile successfully.
|
||||
return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
|
||||
|
||||
## Gets a list of profile writer plugins
|
||||
# \return List of tuples of (plugin_id, meta_data).
|
||||
def _getIOPlugins(self, io_type):
|
||||
plugin_registry = PluginRegistry.getInstance()
|
||||
active_plugin_ids = plugin_registry.getActivePlugins()
|
||||
|
||||
result = []
|
||||
for plugin_id in active_plugin_ids:
|
||||
meta_data = plugin_registry.getMetaData(plugin_id)
|
||||
if io_type in meta_data:
|
||||
result.append( (plugin_id, meta_data) )
|
||||
return result
|
||||
|
||||
## Gets the active definition
|
||||
# \return the active definition object or None if there is no definition
|
||||
def _activeDefinition(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
definition = global_container_stack.getBottom()
|
||||
if definition:
|
||||
return definition
|
||||
return None
|
||||
|
||||
## Returns true if the current machine requires its own materials
|
||||
# \return True if the current machine requires its own materials
|
||||
def _machineHasOwnMaterials(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
return global_container_stack.getMetaDataEntry("has_materials", False)
|
||||
return False
|
||||
|
||||
## Gets the ID of the active material
|
||||
# \return the ID of the active material or the empty string
|
||||
def _activeMaterialId(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
material = global_container_stack.findContainer({"type": "material"})
|
||||
if material:
|
||||
return material.getId()
|
||||
return ""
|
||||
|
||||
## Returns true if the current machien requires its own quality profiles
|
||||
# \return true if the current machien requires its own quality profiles
|
||||
def _machineHasOwnQualities(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
|
||||
return False
|
|
@ -21,6 +21,9 @@ class CuraSplashScreen(QSplashScreen):
|
|||
painter.setPen(QColor(0, 0, 0, 255))
|
||||
|
||||
version = Application.getInstance().getVersion().split("-")
|
||||
buildtype = Application.getInstance().getBuildType()
|
||||
if buildtype:
|
||||
version[0] += " (%s)" %(buildtype)
|
||||
|
||||
painter.setFont(QFont("Proxima Nova Rg", 20 ))
|
||||
painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0])
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
CuraVersion = "@CURA_VERSION@"
|
||||
CuraBuildType = "@CURA_BUILDTYPE@"
|
221
cura/ExtruderManager.py
Normal file
221
cura/ExtruderManager.py
Normal file
|
@ -0,0 +1,221 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant #For communicating data and events to Qt.
|
||||
|
||||
import UM.Application #To get the global container stack to find the current machine.
|
||||
import UM.Logger
|
||||
import UM.Settings.ContainerRegistry #Finding containers by ID.
|
||||
|
||||
|
||||
## Manages all existing extruder stacks.
|
||||
#
|
||||
# This keeps a list of extruder stacks for each machine.
|
||||
class ExtruderManager(QObject):
|
||||
## Signal to notify other components when the list of extruders changes.
|
||||
extrudersChanged = pyqtSignal(QVariant)
|
||||
|
||||
## Notify when the user switches the currently active extruder.
|
||||
activeExtruderChanged = pyqtSignal()
|
||||
|
||||
## Registers listeners and such to listen to changes to the extruders.
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
|
||||
self._active_extruder_index = 0
|
||||
UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders)
|
||||
|
||||
## Gets the unique identifier of the currently active extruder stack.
|
||||
#
|
||||
# The currently active extruder stack is the stack that is currently being
|
||||
# edited.
|
||||
#
|
||||
# \return The unique ID of the currently active extruder stack.
|
||||
@pyqtProperty(str, notify = activeExtruderChanged)
|
||||
def activeExtruderStackId(self):
|
||||
if not UM.Application.getInstance().getGlobalContainerStack():
|
||||
return None #No active machine, so no active extruder.
|
||||
try:
|
||||
return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)].getId()
|
||||
except KeyError: #Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
|
||||
return None
|
||||
|
||||
## The instance of the singleton pattern.
|
||||
#
|
||||
# It's None if the extruder manager hasn't been created yet.
|
||||
__instance = None
|
||||
|
||||
## Gets an instance of the extruder manager, or creates one if no instance
|
||||
# exists yet.
|
||||
#
|
||||
# This is an implementation of singleton. If an extruder manager already
|
||||
# exists, it is re-used.
|
||||
#
|
||||
# \return The extruder manager.
|
||||
@classmethod
|
||||
def getInstance(cls):
|
||||
if not cls.__instance:
|
||||
cls.__instance = ExtruderManager()
|
||||
return cls.__instance
|
||||
|
||||
## Changes the active extruder by index.
|
||||
#
|
||||
# \param index The index of the new active extruder.
|
||||
@pyqtSlot(int)
|
||||
def setActiveExtruderIndex(self, index):
|
||||
self._active_extruder_index = index
|
||||
self.activeExtruderChanged.emit()
|
||||
|
||||
def getActiveExtruderStack(self):
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
global_definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
if global_definition_container:
|
||||
if global_definition_container.getId() in self._extruder_trains:
|
||||
if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]:
|
||||
return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)]
|
||||
|
||||
|
||||
## Adds all extruders of a specific machine definition to the extruder
|
||||
# manager.
|
||||
#
|
||||
# \param machine_definition The machine to add the extruders for.
|
||||
def addMachineExtruders(self, machine_definition):
|
||||
machine_id = machine_definition.getId()
|
||||
if machine_id not in self._extruder_trains:
|
||||
self._extruder_trains[machine_id] = { }
|
||||
|
||||
container_registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
if not container_registry: #Then we shouldn't have any machine definition either. In any case, there are no extruder trains then so bye bye.
|
||||
return
|
||||
|
||||
#Add the extruder trains that don't exist yet.
|
||||
for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()):
|
||||
position = extruder_definition.getMetaDataEntry("position", None)
|
||||
if not position:
|
||||
UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId())
|
||||
if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet.
|
||||
self.createExtruderTrain(extruder_definition, machine_definition, position)
|
||||
|
||||
#Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId())
|
||||
for extruder_train in extruder_trains:
|
||||
self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
|
||||
|
||||
#Ensure that the extruder train stacks are linked to global stack.
|
||||
extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
|
||||
|
||||
if extruder_trains:
|
||||
self.extrudersChanged.emit(machine_definition)
|
||||
|
||||
## Creates a container stack for an extruder train.
|
||||
#
|
||||
# The container stack has an extruder definition at the bottom, which is
|
||||
# linked to a machine definition. Then it has a nozzle profile, a material
|
||||
# profile, a quality profile and a user profile, in that order.
|
||||
#
|
||||
# The resulting container stack is added to the registry.
|
||||
#
|
||||
# \param extruder_definition The extruder to create the extruder train
|
||||
# for.
|
||||
# \param machine_definition The machine that the extruder train belongs
|
||||
# to.
|
||||
# \param position The position of this extruder train in the extruder
|
||||
# slots of the machine.
|
||||
def createExtruderTrain(self, extruder_definition, machine_definition, position):
|
||||
#Cache some things.
|
||||
container_registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
machine_id = machine_definition.getId()
|
||||
|
||||
#Create a container stack for this extruder.
|
||||
extruder_stack_id = container_registry.uniqueName(extruder_definition.getId())
|
||||
container_stack = UM.Settings.ContainerStack(extruder_stack_id)
|
||||
container_stack.setName(extruder_definition.getName()) #Take over the display name to display the stack with.
|
||||
container_stack.addMetaDataEntry("type", "extruder_train")
|
||||
container_stack.addMetaDataEntry("machine", machine_definition.getId())
|
||||
container_stack.addMetaDataEntry("position", position)
|
||||
container_stack.addContainer(extruder_definition)
|
||||
|
||||
#Find the nozzle to use for this extruder.
|
||||
nozzle = container_registry.getEmptyInstanceContainer()
|
||||
if machine_definition.getMetaDataEntry("has_nozzles", default = "False") == "True":
|
||||
#First add any nozzle. Later, overwrite with preference if the preference is valid.
|
||||
nozzles = container_registry.findInstanceContainers(machine = machine_id, type = "nozzle")
|
||||
if len(nozzles) >= 1:
|
||||
nozzle = nozzles[0]
|
||||
preferred_nozzle_id = machine_definition.getMetaDataEntry("preferred_nozzle")
|
||||
if preferred_nozzle_id:
|
||||
preferred_nozzles = container_registry.findInstanceContainers(id = preferred_nozzle_id, type = "nozzle")
|
||||
if len(preferred_nozzles) >= 1:
|
||||
nozzle = preferred_nozzles[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred nozzle \"%s\" of machine %s doesn't exist or is not a nozzle profile.", preferred_nozzle_id, machine_id)
|
||||
#And leave it at the default nozzle.
|
||||
container_stack.addContainer(nozzle)
|
||||
|
||||
#Find a material to use for this nozzle.
|
||||
material = container_registry.getEmptyInstanceContainer()
|
||||
if machine_definition.getMetaDataEntry("has_materials", default = "False") == "True":
|
||||
#First add any material. Later, overwrite with preference if the preference is valid.
|
||||
if machine_definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True":
|
||||
materials = container_registry.findInstanceContainers(type = "material", machine = machine_id, nozzle = nozzle.getId())
|
||||
else:
|
||||
materials = container_registry.findInstanceContainers(type = "material", machine = machine_id)
|
||||
if len(materials) >= 1:
|
||||
material = materials[0]
|
||||
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material_id:
|
||||
preferred_materials = container_registry.findInstanceContainers(id = preferred_material_id, type = "material")
|
||||
if len(preferred_materials) >= 1:
|
||||
material = preferred_materials[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
|
||||
#And leave it at the default material.
|
||||
container_stack.addContainer(material)
|
||||
|
||||
#Find a quality to use for this extruder.
|
||||
quality = container_registry.getEmptyInstanceContainer()
|
||||
|
||||
#First add any quality. Later, overwrite with preference if the preference is valid.
|
||||
qualities = container_registry.findInstanceContainers(type = "quality")
|
||||
if len(qualities) >= 1:
|
||||
quality = qualities[0]
|
||||
preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality_id:
|
||||
preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id, type = "quality")
|
||||
if len(preferred_quality) >= 1:
|
||||
quality = preferred_quality[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality_id, machine_id)
|
||||
#And leave it at the default quality.
|
||||
container_stack.addContainer(quality)
|
||||
|
||||
user_profile = container_registry.findInstanceContainers(id = extruder_stack_id + "_current_settings")
|
||||
if user_profile: #There was already a user profile, loaded from settings.
|
||||
user_profile = user_profile[0]
|
||||
else:
|
||||
user_profile = UM.Settings.InstanceContainer(extruder_stack_id + "_current_settings") #Add an empty user profile.
|
||||
user_profile.addMetaDataEntry("type", "user")
|
||||
user_profile.setDefinition(machine_definition)
|
||||
container_registry.addContainer(user_profile)
|
||||
container_stack.addContainer(user_profile)
|
||||
|
||||
container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
|
||||
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
## Generates extruders for a specific machine.
|
||||
#
|
||||
# \param machine_id The machine to get the extruders of.
|
||||
def getMachineExtruders(self, machine_id):
|
||||
if machine_id not in self._extruder_trains:
|
||||
UM.Logger.log("w", "Tried to get the extruder trains for machine %s, which doesn't exist.", machine_id)
|
||||
return
|
||||
for name in self._extruder_trains[machine_id]:
|
||||
yield self._extruder_trains[machine_id][name]
|
||||
|
||||
## Adds the extruders of the currently active machine.
|
||||
def _addCurrentMachineExtruders(self):
|
||||
global_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack and global_stack.getBottom():
|
||||
self.addMachineExtruders(global_stack.getBottom())
|
104
cura/ExtrudersModel.py
Normal file
104
cura/ExtrudersModel.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
|
||||
|
||||
import cura.ExtruderManager
|
||||
import UM.Qt.ListModel
|
||||
|
||||
## Model that holds extruders.
|
||||
#
|
||||
# This model is designed for use by any list of extruders, but specifically
|
||||
# intended for drop-down lists of the current machine's extruders in place of
|
||||
# settings.
|
||||
class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
# The ID of the container stack for the extruder.
|
||||
IdRole = Qt.UserRole + 1
|
||||
|
||||
## Human-readable name of the extruder.
|
||||
NameRole = Qt.UserRole + 2
|
||||
|
||||
## Colour of the material loaded in the extruder.
|
||||
ColourRole = Qt.UserRole + 3
|
||||
|
||||
## Index of the extruder, which is also the value of the setting itself.
|
||||
#
|
||||
# An index of 0 indicates the first extruder, an index of 1 the second
|
||||
# one, and so on. This is the value that will be saved in instance
|
||||
# containers.
|
||||
IndexRole = Qt.UserRole + 4
|
||||
|
||||
## List of colours to display if there is no material or the material has no known
|
||||
# colour.
|
||||
defaultColours = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
|
||||
|
||||
## Initialises the extruders model, defining the roles and listening for
|
||||
# changes in the data.
|
||||
#
|
||||
# \param parent Parent QtObject of this list.
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.ColourRole, "colour")
|
||||
self.addRoleName(self.IndexRole, "index")
|
||||
|
||||
self._add_global = False
|
||||
|
||||
#Listen to changes.
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general.
|
||||
UM.Application.globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes.
|
||||
self._updateExtruders()
|
||||
|
||||
def setAddGlobal(self, add):
|
||||
if add != self._add_global:
|
||||
self._add_global = add
|
||||
self._updateExtruders()
|
||||
self.addGlobalChanged.emit()
|
||||
|
||||
addGlobalChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, fset = setAddGlobal, notify = addGlobalChanged)
|
||||
def addGlobal(self):
|
||||
return self._add_global
|
||||
|
||||
## Update the list of extruders.
|
||||
#
|
||||
# This should be called whenever the list of extruders changes.
|
||||
def _updateExtruders(self):
|
||||
self.clear()
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
return #There is no machine to get the extruders of.
|
||||
|
||||
if self._add_global:
|
||||
material = global_container_stack.findContainer({ "type": "material" })
|
||||
colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0]
|
||||
item = {
|
||||
"id": global_container_stack.getId(),
|
||||
"name": "Global",
|
||||
"colour": colour,
|
||||
"index": -1
|
||||
}
|
||||
self.appendItem(item)
|
||||
|
||||
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
||||
material = extruder.findContainer({ "type": "material" })
|
||||
position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition.
|
||||
try:
|
||||
position = int(position)
|
||||
except ValueError: #Not a proper int.
|
||||
position = -1
|
||||
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else defaultColours[0]
|
||||
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour
|
||||
item = { #Construct an item with only the relevant information.
|
||||
"id": extruder.getId(),
|
||||
"name": extruder.getName(),
|
||||
"colour": colour,
|
||||
"index": position
|
||||
}
|
||||
self.appendItem(item)
|
||||
|
||||
self.sort(lambda item: item["index"])
|
552
cura/MachineManagerModel.py
Normal file
552
cura/MachineManagerModel.py
Normal file
|
@ -0,0 +1,552 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
import UM.Settings
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from . import ExtruderManager
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class MachineManagerModel(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._active_container_stack = None
|
||||
self._global_container_stack = None
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._global_stack_valid = None
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
self.globalContainerChanged.connect(self._onActiveExtruderStackChanged)
|
||||
self._onActiveExtruderStackChanged()
|
||||
|
||||
## When the global container is changed, active material probably needs to be updated.
|
||||
self.globalContainerChanged.connect(self.activeMaterialChanged)
|
||||
self.globalContainerChanged.connect(self.activeVariantChanged)
|
||||
self.globalContainerChanged.connect(self.activeQualityChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
|
||||
|
||||
self.globalContainerChanged.connect(self.activeStackChanged)
|
||||
self.globalValueChanged.connect(self.activeStackChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
|
||||
|
||||
self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0]
|
||||
self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0]
|
||||
self._empty_quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_machine", "")
|
||||
|
||||
active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
|
||||
|
||||
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
|
||||
if active_machine_id != "":
|
||||
# An active machine was saved, so restore it.
|
||||
self.setActiveMachine(active_machine_id)
|
||||
pass
|
||||
|
||||
globalContainerChanged = pyqtSignal()
|
||||
activeMaterialChanged = pyqtSignal()
|
||||
activeVariantChanged = pyqtSignal()
|
||||
activeQualityChanged = pyqtSignal()
|
||||
activeStackChanged = pyqtSignal()
|
||||
|
||||
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
|
||||
globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed
|
||||
|
||||
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
|
||||
|
||||
outputDevicesChanged = pyqtSignal()
|
||||
|
||||
def _onOutputDevicesChanged(self):
|
||||
self.outputDevicesChanged.emit()
|
||||
|
||||
def _onGlobalPropertyChanged(self, key, property_name):
|
||||
if property_name == "value":
|
||||
self.globalValueChanged.emit()
|
||||
if property_name == "validationState":
|
||||
if self._global_stack_valid:
|
||||
changed_validation_state = self._active_container_stack.getProperty(key, property_name)
|
||||
if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
self._global_stack_valid = False
|
||||
self.globalValidationChanged.emit()
|
||||
else:
|
||||
new_validation_state = self._checkStackForErrors(self._active_container_stack)
|
||||
if new_validation_state:
|
||||
self._global_stack_valid = True
|
||||
self.globalValidationChanged.emit()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
|
||||
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
if self._global_container_stack:
|
||||
Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId())
|
||||
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
|
||||
|
||||
def _onActiveExtruderStackChanged(self):
|
||||
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
|
||||
self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
|
||||
|
||||
self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
else:
|
||||
self._active_container_stack = self._global_container_stack
|
||||
|
||||
def _onInstanceContainersChanged(self, container):
|
||||
container_type = container.getMetaDataEntry("type")
|
||||
if container_type == "material":
|
||||
self.activeMaterialChanged.emit()
|
||||
elif container_type == "variant":
|
||||
self.activeVariantChanged.emit()
|
||||
elif container_type == "quality":
|
||||
self.activeQualityChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveMachine(self, stack_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = stack_id)
|
||||
if containers:
|
||||
Application.getInstance().setGlobalContainerStack(containers[0])
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def addMachine(self, name, definition_id):
|
||||
definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
|
||||
if definitions:
|
||||
definition = definitions[0]
|
||||
name = self._createUniqueName("machine", "", name, definition.getName())
|
||||
new_global_stack = UM.Settings.ContainerStack(name)
|
||||
new_global_stack.addMetaDataEntry("type", "machine")
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack)
|
||||
|
||||
variant_instance_container = self._updateVariantContainer(definition)
|
||||
material_instance_container = self._updateMaterialContainer(definition, variant_instance_container)
|
||||
quality_instance_container = self._updateQualityContainer(definition, material_instance_container)
|
||||
|
||||
current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings")
|
||||
current_settings_instance_container.addMetaDataEntry("machine", name)
|
||||
current_settings_instance_container.addMetaDataEntry("type", "user")
|
||||
current_settings_instance_container.setDefinition(definitions[0])
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(current_settings_instance_container)
|
||||
|
||||
# If a definition is found, its a list. Should only have one item.
|
||||
new_global_stack.addContainer(definition)
|
||||
if variant_instance_container:
|
||||
new_global_stack.addContainer(variant_instance_container)
|
||||
if material_instance_container:
|
||||
new_global_stack.addContainer(material_instance_container)
|
||||
if quality_instance_container:
|
||||
new_global_stack.addContainer(quality_instance_container)
|
||||
new_global_stack.addContainer(current_settings_instance_container)
|
||||
|
||||
ExtruderManager.ExtruderManager.getInstance().addMachineExtruders(definition)
|
||||
|
||||
Application.getInstance().setGlobalContainerStack(new_global_stack)
|
||||
|
||||
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
|
||||
def printerOutputDevices(self):
|
||||
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
|
||||
|
||||
## Create a name that is not empty and unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
# \param current_name \type{} Current name of the container, which may be an acceptable option
|
||||
# \param new_name \type{string} Base name, which may not be unique
|
||||
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
|
||||
# \return \type{string} Name that is unique for the specified type and name/id
|
||||
def _createUniqueName(self, container_type, current_name, new_name, fallback_name):
|
||||
return UM.Settings.ContainerRegistry.getInstance().createUniqueName(container_type, current_name, new_name, fallback_name)
|
||||
|
||||
## Convenience function to check if a stack has errors.
|
||||
def _checkStackForErrors(self, stack):
|
||||
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):
|
||||
return True
|
||||
return False
|
||||
|
||||
## Remove all instances from the top instanceContainer (effectively removing all user-changed settings)
|
||||
@pyqtSlot()
|
||||
def clearUserSettings(self):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
|
||||
self.blurSettings.emit()
|
||||
user_settings = self._active_container_stack.getTop()
|
||||
user_settings.clear()
|
||||
|
||||
## Check if the global_container has instances in the user container
|
||||
@pyqtProperty(bool, notify = activeStackChanged)
|
||||
def hasUserSettings(self):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
|
||||
user_settings = self._active_container_stack.getTop().findInstances(**{})
|
||||
return len(user_settings) != 0
|
||||
|
||||
## Check if the global profile does not contain error states
|
||||
# Note that the _global_stack_valid is cached due to performance issues
|
||||
# Calling _checkStackForErrors on every change is simply too expensive
|
||||
@pyqtProperty(bool, notify = globalValidationChanged)
|
||||
def isGlobalStackValid(self):
|
||||
return self._global_stack_valid
|
||||
|
||||
@pyqtProperty(str, notify = activeStackChanged)
|
||||
def activeUserProfileId(self):
|
||||
if self._active_container_stack:
|
||||
return self._active_container_stack.getTop().getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeMachineName(self):
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getName()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeMachineId(self):
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeMaterialChanged)
|
||||
def activeMaterialName(self):
|
||||
if self._active_container_stack:
|
||||
material = self._active_container_stack.findContainer({"type":"material"})
|
||||
if material:
|
||||
return material.getName()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeMaterialChanged)
|
||||
def activeMaterialId(self):
|
||||
if self._active_container_stack:
|
||||
material = self._active_container_stack.findContainer({"type": "material"})
|
||||
if material:
|
||||
return material.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityName(self):
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if quality:
|
||||
return quality.getName()
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityId(self):
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if quality:
|
||||
return quality.getId()
|
||||
return ""
|
||||
|
||||
## Check if a container is read_only
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isReadOnly(self, container_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return True
|
||||
return containers[0].isReadOnly()
|
||||
|
||||
@pyqtSlot(result = str)
|
||||
def newQualityContainerFromQualityAndUser(self):
|
||||
new_container_id = self.duplicateContainer(self.activeQualityId)
|
||||
if new_container_id == "":
|
||||
return
|
||||
self.blurSettings.emit()
|
||||
self.setActiveQuality(new_container_id)
|
||||
self.updateQualityContainerFromUserContainer()
|
||||
|
||||
|
||||
@pyqtSlot(str, result=str)
|
||||
def duplicateContainer(self, container_id):
|
||||
if not self._active_container_stack:
|
||||
return ""
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if containers:
|
||||
new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile"))
|
||||
|
||||
new_container = InstanceContainer("")
|
||||
|
||||
## Copy all values
|
||||
new_container.deserialize(containers[0].serialize())
|
||||
|
||||
new_container.setReadOnly(False)
|
||||
new_container.setName(new_name)
|
||||
new_container._id = new_name
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)
|
||||
return new_name
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def renameQualityContainer(self, container_id, new_name):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality")
|
||||
if containers:
|
||||
new_name = self._createUniqueName("quality", containers[0].getName(), new_name,
|
||||
catalog.i18nc("@label", "Custom profile"))
|
||||
|
||||
if containers[0].getName() == new_name:
|
||||
# Nothing to do.
|
||||
return
|
||||
|
||||
# As we also want the id of the container to be changed (so that profile name is the name of the file
|
||||
# on disk. We need to create a new instance and remove it (so the old file of the container is removed)
|
||||
# If we don't do that, we might get duplicates & other weird issues.
|
||||
new_container = InstanceContainer("")
|
||||
new_container.deserialize(containers[0].serialize())
|
||||
|
||||
# Actually set the name
|
||||
new_container.setName(new_name)
|
||||
new_container._id = new_name # Todo: Fix proper id change function for this.
|
||||
|
||||
# Add the "new" container.
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)
|
||||
|
||||
# Ensure that the renamed profile is saved -before- we remove the old profile.
|
||||
Application.getInstance().saveSettings()
|
||||
|
||||
# Actually set & remove new / old quality.
|
||||
self.setActiveQuality(new_name)
|
||||
self.removeQualityContainer(containers[0].getId())
|
||||
|
||||
@pyqtSlot(str)
|
||||
def removeQualityContainer(self, container_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
|
||||
# If the container that is being removed is the currently active container, set another machine as the active container
|
||||
activate_new_container = container_id == self.activeQualityId
|
||||
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(container_id)
|
||||
|
||||
if activate_new_container:
|
||||
definition_id = "fdmprinter" if not self.filterQualityByMachine else self.activeDefinitionId
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "quality", definition = definition_id)
|
||||
if containers:
|
||||
self.setActiveQuality(containers[0].getId())
|
||||
self.activeQualityChanged.emit()
|
||||
|
||||
|
||||
@pyqtSlot()
|
||||
def updateQualityContainerFromUserContainer(self):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
user_settings = self._active_container_stack.getTop()
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
for key in user_settings.getAllKeys():
|
||||
quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
|
||||
self.clearUserSettings() # As all users settings are noq a quality, remove them.
|
||||
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveMaterial(self, material_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = material_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
|
||||
old_material = self._active_container_stack.findContainer({"type":"material"})
|
||||
if old_material:
|
||||
material_index = self._active_container_stack.getContainerIndex(old_material)
|
||||
self._active_container_stack.replaceContainer(material_index, containers[0])
|
||||
|
||||
self.setActiveQuality(self._updateQualityContainer(self._active_container_stack.getBottom(), containers[0]).id)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveVariant(self, variant_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
|
||||
old_variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
if old_variant:
|
||||
variant_index = self._active_container_stack.getContainerIndex(old_variant)
|
||||
self._active_container_stack.replaceContainer(variant_index, containers[0])
|
||||
|
||||
self.setActiveMaterial(self._updateMaterialContainer(self._active_container_stack.getBottom(), containers[0]).id)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveQuality(self, quality_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
|
||||
old_quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if old_quality:
|
||||
quality_index = self._active_container_stack.getContainerIndex(old_quality)
|
||||
self._active_container_stack.replaceContainer(quality_index, containers[0])
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantName(self):
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
if variant:
|
||||
return variant.getName()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantId(self):
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
if variant:
|
||||
return variant.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeDefinitionId(self):
|
||||
if self._global_container_stack:
|
||||
definition = self._global_container_stack.getBottom()
|
||||
if definition:
|
||||
return definition.id
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def renameMachine(self, machine_id, new_name):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
|
||||
if containers:
|
||||
new_name = self._createUniqueName("machine", containers[0].getName(), new_name, containers[0].getBottom().getName())
|
||||
containers[0].setName(new_name)
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def removeMachine(self, machine_id):
|
||||
# If the machine that is being removed is the currently active machine, set another machine as the active machine
|
||||
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
|
||||
|
||||
current_settings_id = machine_id + "_current_settings"
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = current_settings_id)
|
||||
for container in containers:
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId())
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(machine_id)
|
||||
|
||||
if activate_new_machine:
|
||||
stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
if stacks:
|
||||
Application.getInstance().setGlobalContainerStack(stacks[0])
|
||||
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def hasMaterials(self):
|
||||
if self._global_container_stack:
|
||||
return bool(self._global_container_stack.getMetaDataEntry("has_materials", False))
|
||||
|
||||
return False
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def hasVariants(self):
|
||||
if self._global_container_stack:
|
||||
return bool(self._global_container_stack.getMetaDataEntry("has_variants", False))
|
||||
|
||||
return False
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def filterMaterialsByMachine(self):
|
||||
if self._global_container_stack:
|
||||
return bool(self._global_container_stack.getMetaDataEntry("has_machine_materials", False))
|
||||
|
||||
return False
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def filterQualityByMachine(self):
|
||||
if self._global_container_stack:
|
||||
return bool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False))
|
||||
|
||||
return False
|
||||
|
||||
def _updateVariantContainer(self, definition):
|
||||
if not definition.getMetaDataEntry("has_variants"):
|
||||
return self._empty_variant_container
|
||||
|
||||
containers = []
|
||||
preferred_variant = definition.getMetaDataEntry("preferred_variant")
|
||||
if preferred_variant:
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = definition.id, id = preferred_variant)
|
||||
|
||||
if not containers:
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = definition.id)
|
||||
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return self._empty_variant_container
|
||||
|
||||
def _updateMaterialContainer(self, definition, variant_container = None):
|
||||
if not definition.getMetaDataEntry("has_materials"):
|
||||
return self._empty_material_container
|
||||
|
||||
search_criteria = { "type": "material" }
|
||||
|
||||
if definition.getMetaDataEntry("has_machine_materials"):
|
||||
search_criteria["definition"] = definition.id
|
||||
|
||||
if definition.getMetaDataEntry("has_variants") and variant_container:
|
||||
search_criteria["variant"] = variant_container.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material:
|
||||
search_criteria["id"] = preferred_material
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return self._empty_material_container
|
||||
|
||||
def _updateQualityContainer(self, definition, material_container = None):
|
||||
search_criteria = { "type": "quality" }
|
||||
|
||||
if definition.getMetaDataEntry("has_machine_quality"):
|
||||
search_criteria["definition"] = definition.id
|
||||
|
||||
if definition.getMetaDataEntry("has_materials") and material_container:
|
||||
search_criteria["material"] = material_container.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality:
|
||||
search_criteria["id"] = preferred_quality
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return self._empty_quality_container
|
||||
|
||||
def createMachineManagerModel(engine, script_engine):
|
||||
return MachineManagerModel()
|
|
@ -1,14 +1,17 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.Duration import Duration
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
import math
|
||||
import os.path
|
||||
import unicodedata
|
||||
|
||||
## A class for processing and calculating minimum, current and maximum print time.
|
||||
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
|
||||
#
|
||||
# This class contains all the logic relating to calculation and slicing for the
|
||||
# time/quality slider concept. It is a rather tricky combination of event handling
|
||||
|
@ -22,6 +25,8 @@ import math
|
|||
# - When that is done, we update the minimum print time and start the final slice pass, the "high quality settings pass".
|
||||
# - When the high quality pass is done, we update the maximum print time.
|
||||
#
|
||||
# This class also mangles the current machine name and the filename of the first loaded mesh into a job name.
|
||||
# This job name is requested by the JobSpecs qml file.
|
||||
class PrintInformation(QObject):
|
||||
class SlicePass:
|
||||
CurrentSettings = 1
|
||||
|
@ -45,14 +50,20 @@ class PrintInformation(QObject):
|
|||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
|
||||
self._job_name = ""
|
||||
self._abbr_machine = ""
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
|
||||
Application.getInstance().fileLoaded.connect(self.setJobName)
|
||||
|
||||
currentPrintTimeChanged = pyqtSignal()
|
||||
|
||||
|
||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
|
||||
materialAmountChanged = pyqtSignal()
|
||||
|
||||
|
||||
@pyqtProperty(float, notify = materialAmountChanged)
|
||||
def materialAmount(self):
|
||||
return self._material_amount
|
||||
|
@ -63,6 +74,49 @@ class PrintInformation(QObject):
|
|||
self.currentPrintTimeChanged.emit()
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
r = Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("material_diameter") / 2
|
||||
r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
||||
self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2)
|
||||
self.materialAmountChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
# when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
|
||||
# extension. This cuts the extension off if necessary.
|
||||
name = os.path.splitext(name)[0]
|
||||
if self._job_name != name:
|
||||
self._job_name = name
|
||||
self.jobNameChanged.emit()
|
||||
|
||||
jobNameChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(str, notify = jobNameChanged)
|
||||
def jobName(self):
|
||||
return self._job_name
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def createJobName(self, base_name):
|
||||
base_name = self._stripAccents(base_name)
|
||||
if Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
return self._abbr_machine + "_" + base_name
|
||||
else:
|
||||
return base_name
|
||||
|
||||
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
||||
# Called each time the global stack is switched
|
||||
def _setAbbreviatedMachineName(self):
|
||||
global_stack_name = Application.getInstance().getGlobalContainerStack().getName()
|
||||
split_name = global_stack_name.split(" ")
|
||||
abbr_machine = ""
|
||||
for word in split_name:
|
||||
if word.lower() == "ultimaker":
|
||||
abbr_machine += "UM"
|
||||
elif word.isdigit():
|
||||
abbr_machine += word
|
||||
else:
|
||||
abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0]
|
||||
|
||||
self._abbr_machine = abbr_machine
|
||||
|
||||
## Utility method that strips accents from characters (eg: â -> a)
|
||||
def _stripAccents(self, str):
|
||||
return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
|
|
@ -3,6 +3,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
|||
from enum import IntEnum # For the connection state tracking.
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.Signal import signalemitter
|
||||
|
||||
## Printer output device adds extra interface options on top of output device.
|
||||
#
|
||||
|
@ -13,7 +14,8 @@ from UM.Logger import Logger
|
|||
# functions to actually have the implementation.
|
||||
#
|
||||
# For all other uses it should be used in the same way as a "regular" OutputDevice.
|
||||
class PrinterOutputDevice(OutputDevice, QObject):
|
||||
@signalemitter
|
||||
class PrinterOutputDevice(QObject, OutputDevice):
|
||||
def __init__(self, device_id, parent = None):
|
||||
super().__init__(device_id = device_id, parent = parent)
|
||||
|
||||
|
|
17
cura/ProfileReader.py
Normal file
17
cura/ProfileReader.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.PluginObject import PluginObject
|
||||
|
||||
## A type of plug-ins that reads profiles from a file.
|
||||
#
|
||||
# The profile is then stored as instance container of the type user profile.
|
||||
class ProfileReader(PluginObject):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
## Read profile data from a file and return a filled profile.
|
||||
#
|
||||
# \return \type{Profile} The profile that was obtained from the file.
|
||||
def read(self, file_name):
|
||||
raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")
|
25
cura/ProfileWriter.py
Normal file
25
cura/ProfileWriter.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.PluginObject import PluginObject
|
||||
|
||||
## Base class for profile writer plugins.
|
||||
#
|
||||
# This class defines a write() function to write profiles to files with.
|
||||
class ProfileWriter(PluginObject):
|
||||
## Initialises the profile writer.
|
||||
#
|
||||
# This currently doesn't do anything since the writer is basically static.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
## Writes a profile to the specified file path.
|
||||
#
|
||||
# The profile writer may write its own file format to the specified file.
|
||||
#
|
||||
# \param path \type{string} The file to output to.
|
||||
# \param profile \type{Profile} The profile to write to the file.
|
||||
# \return \code True \endcode if the writing was successful, or \code
|
||||
# False \endcode if it wasn't.
|
||||
def write(self, path, node):
|
||||
raise NotImplementedError("Profile writer plugin was not correctly implemented. No write was specified.")
|
50
cura/SetParentOperation.py
Normal file
50
cura/SetParentOperation.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Operations import Operation
|
||||
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
## An operation that parents a scene node to another scene node.
|
||||
|
||||
class SetParentOperation(Operation.Operation):
|
||||
## Initialises this SetParentOperation.
|
||||
#
|
||||
# \param node The node which will be reparented.
|
||||
# \param parent_node The node which will be the parent.
|
||||
def __init__(self, node, parent_node):
|
||||
super().__init__()
|
||||
self._node = node
|
||||
self._parent = parent_node
|
||||
self._old_parent = node.getParent() # To restore the previous parent in case of an undo.
|
||||
|
||||
## Undoes the set-parent operation, restoring the old parent.
|
||||
def undo(self):
|
||||
self._set_parent(self._old_parent)
|
||||
|
||||
## Re-applies the set-parent operation.
|
||||
def redo(self):
|
||||
self._set_parent(self._parent)
|
||||
|
||||
## Sets the parent of the node while applying transformations to the world-transform of the node stays the same.
|
||||
#
|
||||
# \param new_parent The new parent. Note: this argument can be None, which would hide the node from the scene.
|
||||
def _set_parent(self, new_parent):
|
||||
if new_parent:
|
||||
self._node.setPosition(self._node.getWorldPosition() - new_parent.getWorldPosition())
|
||||
current_parent = self._node.getParent()
|
||||
if current_parent:
|
||||
self._node.scale(current_parent.getScale() / new_parent.getScale())
|
||||
self._node.rotate(current_parent.getOrientation())
|
||||
else:
|
||||
self._node.scale(Vector(1, 1, 1) / new_parent.getScale())
|
||||
self._node.rotate(new_parent.getOrientation().getInverse())
|
||||
|
||||
self._node.setParent(new_parent)
|
||||
|
||||
## Returns a programmer-readable representation of this operation.
|
||||
#
|
||||
# \return A programmer-readable representation of this operation.
|
||||
def __repr__(self):
|
||||
return "SetParentOperation(node = {0}, parent_node={1})".format(self._node, self._parent)
|
81
cura/SettingOverrideDecorator.py
Normal file
81
cura/SettingOverrideDecorator.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
from UM.Signal import Signal, signalemitter
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
import UM.Logger
|
||||
|
||||
from UM.Application import Application
|
||||
|
||||
## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding
|
||||
# the linked node. The Stack in question will refer to the global stack (so that settings that are not defined by
|
||||
# this stack still resolve.
|
||||
@signalemitter
|
||||
class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
## Event indicating that the user selected a different extruder.
|
||||
activeExtruderChanged = Signal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._stack = ContainerStack(stack_id = id(self))
|
||||
self._stack.setDirty(False) # This stack does not need to be saved.
|
||||
self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
|
||||
self._stack.addContainer(self._instance)
|
||||
self._extruder_stack = None #Stack upon which our stack is based.
|
||||
|
||||
self._stack.propertyChanged.connect(self._onSettingChanged)
|
||||
|
||||
ContainerRegistry.getInstance().addContainer(self._stack)
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
|
||||
self.activeExtruderChanged.connect(self._updateNextStack)
|
||||
self._updateNextStack()
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
## Create a fresh decorator object
|
||||
deep_copy = SettingOverrideDecorator()
|
||||
## Copy the instance
|
||||
deep_copy._instance = copy.deepcopy(self._instance, memo)
|
||||
## Set the copied instance as the first (and only) instance container of the stack.
|
||||
deep_copy._stack.replaceContainer(0, deep_copy._instance)
|
||||
return deep_copy
|
||||
|
||||
## Gets the currently active extruder to print this object with.
|
||||
#
|
||||
# \return An extruder's container stack.
|
||||
def getActiveExtruder(self):
|
||||
return self._extruder_stack
|
||||
|
||||
def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
|
||||
if property_name == "value": # Only reslice if the value has changed.
|
||||
Application.getInstance().getBackend().forceSlice()
|
||||
|
||||
## Makes sure that the stack upon which the container stack is placed is
|
||||
# kept up to date.
|
||||
def _updateNextStack(self):
|
||||
if self._extruder_stack:
|
||||
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
|
||||
if extruder_stack:
|
||||
old_extruder_stack_id = self._stack.getNextStack().getId()
|
||||
self._stack.setNextStack(extruder_stack[0])
|
||||
if self._stack.getNextStack().getId() != old_extruder_stack_id: #Only reslice if the extruder changed.
|
||||
Application.getInstance().getBackend().forceSlice()
|
||||
else:
|
||||
UM.Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
|
||||
else:
|
||||
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
|
||||
|
||||
## Changes the extruder with which to print this node.
|
||||
#
|
||||
# \param extruder_stack_id The new extruder stack to print with.
|
||||
def setActiveExtruder(self, extruder_stack_id):
|
||||
self._extruder_stack = extruder_stack_id
|
||||
self.activeExtruderChanged.emit()
|
||||
|
||||
def getStack(self):
|
||||
return self._stack
|
|
@ -33,14 +33,18 @@ sys.excepthook = exceptHook
|
|||
# first seems to prevent Sip from going into a state where it
|
||||
# tries to create PyQt objects on a non-main thread.
|
||||
import Arcus #@UnusedImport
|
||||
from UM.Platform import Platform
|
||||
import cura.CuraApplication
|
||||
import cura.CuraContainerRegistry
|
||||
|
||||
if sys.platform == "win32" and hasattr(sys, "frozen"):
|
||||
import os
|
||||
if Platform.isWindows() and hasattr(sys, "frozen"):
|
||||
dirpath = os.path.expanduser("~/AppData/Local/cura/")
|
||||
os.makedirs(dirpath, exist_ok = True)
|
||||
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
|
||||
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
|
||||
|
||||
# Force an instance of CuraContainerRegistry to be created and reused later.
|
||||
cura.CuraContainerRegistry.CuraContainerRegistry.getInstance()
|
||||
|
||||
app = cura.CuraApplication.CuraApplication.getInstance()
|
||||
app.run()
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"mesh_reader": [
|
||||
{
|
||||
|
|
|
@ -15,15 +15,9 @@ class AutoSave(Extension):
|
|||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._triggerTimer)
|
||||
|
||||
machine_manager = Application.getInstance().getMachineManager()
|
||||
|
||||
self._profile = None
|
||||
machine_manager.activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
machine_manager.profileNameChanged.connect(self._triggerTimer)
|
||||
machine_manager.profilesChanged.connect(self._triggerTimer)
|
||||
machine_manager.machineInstanceNameChanged.connect(self._triggerTimer)
|
||||
machine_manager.machineInstancesChanged.connect(self._triggerTimer)
|
||||
self._onActiveProfileChanged()
|
||||
self._global_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10)
|
||||
|
||||
|
@ -38,24 +32,23 @@ class AutoSave(Extension):
|
|||
if not self._saving:
|
||||
self._change_timer.start()
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._triggerTimer)
|
||||
def _onGlobalStackChanged(self):
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self._triggerTimer)
|
||||
self._global_stack.containersChanged.disconnect(self._triggerTimer)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
self._global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._triggerTimer)
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self._triggerTimer)
|
||||
self._global_stack.containersChanged.connect(self._triggerTimer)
|
||||
|
||||
def _onTimeout(self):
|
||||
self._saving = True # To prevent the save process from triggering another autosave.
|
||||
Logger.log("d", "Autosaving preferences, instances and profiles")
|
||||
|
||||
machine_manager = Application.getInstance().getMachineManager()
|
||||
Application.getInstance().saveSettings()
|
||||
|
||||
machine_manager.saveVisibility()
|
||||
machine_manager.saveMachineInstances()
|
||||
machine_manager.saveProfiles()
|
||||
Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences, Application.getInstance().getApplicationName() + ".cfg"))
|
||||
|
||||
self._saving = False
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Automatically saves Preferences, Machines and Profiles after changes."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,8 @@ class ChangeLog(Extension, QObject,):
|
|||
result += "<h1>" + str(version) + "</h1><br>"
|
||||
result += ""
|
||||
for change in logs[version]:
|
||||
result += "<b>" + str(change) + "</b><br>"
|
||||
if str(change) != "":
|
||||
result += "<b>" + str(change) + "</b><br>"
|
||||
for line in logs[version][change]:
|
||||
result += str(line) + "<br>"
|
||||
result += "<br>"
|
||||
|
@ -60,20 +61,21 @@ class ChangeLog(Extension, QObject,):
|
|||
self._change_logs = collections.OrderedDict()
|
||||
with open(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"), "r",-1, "utf-8") as f:
|
||||
open_version = None
|
||||
open_header = None
|
||||
open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog
|
||||
for line in f:
|
||||
line = line.replace("\n","")
|
||||
if "[" in line and "]" in line:
|
||||
line = line.replace("[","")
|
||||
line = line.replace("]","")
|
||||
open_version = Version(line)
|
||||
self._change_logs[Version(line)] = collections.OrderedDict()
|
||||
self._change_logs[open_version] = collections.OrderedDict()
|
||||
elif line.startswith("*"):
|
||||
open_header = line.replace("*","")
|
||||
self._change_logs[open_version][open_header] = []
|
||||
else:
|
||||
if line != "":
|
||||
self._change_logs[open_version][open_header].append(line)
|
||||
elif line != "":
|
||||
if open_header not in self._change_logs[open_version]:
|
||||
self._change_logs[open_version][open_header] = []
|
||||
self._change_logs[open_version][open_header].append(line)
|
||||
|
||||
def _onEngineCreated(self):
|
||||
if not self._version:
|
||||
|
@ -105,4 +107,3 @@ class ChangeLog(Extension, QObject,):
|
|||
self._changelog_context = QQmlContext(Application.getInstance()._engine.rootContext())
|
||||
self._changelog_context.setContextProperty("manager", self)
|
||||
self._changelog_window = component.create(self._changelog_context)
|
||||
#print(self._changelog_window)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[2.1.0]
|
||||
[2.1.2]
|
||||
|
||||
*2.1 Beta release
|
||||
Cura has been completely reengineered from the ground up for an even more seamless integration between hardware, software and materials. Together with its intuitive new user interface, it’s now also ready for any future developments. For the beginner Cura makes 3D printing incredibly easy, and for more advanced users, there are over 140 new customisable settings.
|
||||
|
||||
*Select Multiple Objects
|
||||
|
|
|
@ -13,9 +13,9 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return {"extension": ChangeLog.ChangeLog()}
|
||||
return {"extension": ChangeLog.ChangeLog()}
|
||||
|
|
|
@ -5,12 +5,20 @@ package cura.proto;
|
|||
message ObjectList
|
||||
{
|
||||
repeated Object objects = 1;
|
||||
repeated Setting settings = 2;
|
||||
repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing)
|
||||
}
|
||||
|
||||
message Slice
|
||||
{
|
||||
repeated ObjectList object_lists = 1;
|
||||
repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another
|
||||
SettingList global_settings = 2; // The global settings used for the whole print job
|
||||
repeated Extruder extruders = 3; // The settings sent to each extruder object
|
||||
}
|
||||
|
||||
message Extruder
|
||||
{
|
||||
int32 id = 1;
|
||||
SettingList settings = 2;
|
||||
}
|
||||
|
||||
message Object
|
||||
|
@ -29,10 +37,10 @@ message Progress
|
|||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
float height = 2; // Z position
|
||||
float thickness = 3; // height of a single layer
|
||||
|
||||
repeated Polygon polygons = 4;
|
||||
repeated Polygon polygons = 4; // layer data
|
||||
}
|
||||
|
||||
message Polygon {
|
||||
|
@ -48,19 +56,19 @@ message Polygon {
|
|||
MoveCombingType = 8;
|
||||
MoveRetractionType = 9;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
float line_width = 3;
|
||||
Type type = 1; // Type of move
|
||||
bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used)
|
||||
float line_width = 3; // The width of the line being laid down
|
||||
}
|
||||
|
||||
message GCodeLayer {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message ObjectPrintTime {
|
||||
message ObjectPrintTime { // The print time for the whole print and material estimates for the first extruder
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
float time = 2; // Total time estimate
|
||||
float material_amount = 3; // material used in the first extruder
|
||||
}
|
||||
|
||||
message SettingList {
|
||||
|
@ -68,13 +76,13 @@ message SettingList {
|
|||
}
|
||||
|
||||
message Setting {
|
||||
string name = 1;
|
||||
string name = 1; // Internal key to signify a setting
|
||||
|
||||
bytes value = 2;
|
||||
bytes value = 2; // The value of the setting
|
||||
}
|
||||
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
bytes data = 2; // Header string to be prenpended before the rest of the gcode sent from the engine
|
||||
}
|
||||
|
||||
message SlicingFinished {
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Backend.Backend import Backend
|
||||
from UM.Backend.Backend import Backend, BackendState
|
||||
from UM.Application import Application
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Signal import Signal
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.Bindings.BackendProxy import BackendState #To determine the state of the slicing job.
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
|
||||
from cura.ExtruderManager import ExtruderManager
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from . import ProcessSlicedLayersJob
|
||||
|
@ -29,6 +32,10 @@ catalog = i18nCatalog("cura")
|
|||
|
||||
|
||||
class CuraEngineBackend(Backend):
|
||||
## Starts the back-end plug-in.
|
||||
#
|
||||
# This registers all the signal listeners and prepares for communication
|
||||
# with the back-end in general.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
@ -36,7 +43,7 @@ class CuraEngineBackend(Backend):
|
|||
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
|
||||
if hasattr(sys, "frozen"):
|
||||
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
|
||||
if sys.platform == "win32":
|
||||
if Platform.isWindows():
|
||||
default_engine_location += ".exe"
|
||||
default_engine_location = os.path.abspath(default_engine_location)
|
||||
Preferences.getInstance().addPreference("backend/location", default_engine_location)
|
||||
|
@ -50,19 +57,24 @@ class CuraEngineBackend(Backend):
|
|||
self._onActiveViewChanged()
|
||||
self._stored_layer_data = []
|
||||
|
||||
# When there are current settings and machine instance is changed, there is no profile changed event. We should
|
||||
# pretend there is though.
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveProfileChanged)
|
||||
#Triggers for when to (re)start slicing:
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
self._profile = None
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
self._onActiveProfileChanged()
|
||||
self._active_extruder_stack = None
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
#When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||
#This timer will group them up, and only slice for the last setting changed signal.
|
||||
#TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(500)
|
||||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.timeout.connect(self.slice)
|
||||
|
||||
#Listeners for receiving messages from the back-end.
|
||||
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
|
||||
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
|
||||
self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
|
||||
|
@ -70,39 +82,35 @@ class CuraEngineBackend(Backend):
|
|||
self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage
|
||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._slicing = False
|
||||
self._start_slice_job = None
|
||||
self._restart = False
|
||||
self._enabled = True
|
||||
self._always_restart = True
|
||||
self._slicing = False #Are we currently slicing?
|
||||
self._restart = False #Back-end is currently restarting?
|
||||
self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
|
||||
self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
|
||||
self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.
|
||||
|
||||
self._message = None
|
||||
self._error_message = None #Pop-up message that shows errors.
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
#When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onInstanceChanged)
|
||||
|
||||
## Called when closing the application.
|
||||
#
|
||||
# This function should terminate the engine process.
|
||||
def close(self):
|
||||
# Terminate CuraEngine if it is still running at this point
|
||||
self._terminate()
|
||||
super().close()
|
||||
|
||||
## Get the command that is used to call the engine.
|
||||
# This is usefull for debugging and used to actually start the engine
|
||||
# This is useful for debugging and used to actually start the engine.
|
||||
# \return list of commands and args / parameters.
|
||||
def getEngineCommand(self):
|
||||
active_machine = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
json_path = ""
|
||||
if not active_machine:
|
||||
json_path = Resources.getPath(Resources.MachineDefinitions, "fdmprinter.json")
|
||||
else:
|
||||
json_path = active_machine.getMachineDefinition().getPath()
|
||||
|
||||
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
|
||||
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-vv"]
|
||||
|
||||
## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished.
|
||||
|
@ -113,55 +121,39 @@ class CuraEngineBackend(Backend):
|
|||
## Emitted when the slicing process starts.
|
||||
slicingStarted = Signal()
|
||||
|
||||
## Emitted whne the slicing process is aborted forcefully.
|
||||
## Emitted when the slicing process is aborted forcefully.
|
||||
slicingCancelled = Signal()
|
||||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
self._stored_layer_data = []
|
||||
|
||||
if not self._enabled:
|
||||
if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
|
||||
return
|
||||
|
||||
if self._slicing:
|
||||
if self._slicing: #We were already slicing. Stop the old job.
|
||||
self._terminate()
|
||||
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
|
||||
return
|
||||
|
||||
if self._process_layers_job:
|
||||
if self._process_layers_job: #We were processing layers. Stop that, the layers are going to change soon.
|
||||
self._process_layers_job.abort()
|
||||
self._process_layers_job = None
|
||||
|
||||
if self._profile.hasErrorValue():
|
||||
Logger.log("w", "Profile has error values. Aborting slicing")
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
|
||||
self._message.show()
|
||||
return #No slicing if we have error values since those are by definition illegal values.
|
||||
if self._error_message:
|
||||
self._error_message.hide()
|
||||
|
||||
self.processingProgress.emit(0.0)
|
||||
self.backendStateChange.emit(BackendState.NOT_STARTED)
|
||||
if self._message:
|
||||
self._message.setProgress(-1)
|
||||
#else:
|
||||
# self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
|
||||
# self._message.show()
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
|
||||
self._scene.gcode_list = []
|
||||
self._slicing = True
|
||||
self.slicingStarted.emit()
|
||||
|
||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||
settings_message = self._socket.createMessage("cura.proto.SettingList");
|
||||
self._start_slice_job = StartSliceJob.StartSliceJob(self._profile, slice_message, settings_message)
|
||||
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
|
||||
self._start_slice_job.start()
|
||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
## Terminate the engine process.
|
||||
def _terminate(self):
|
||||
self._slicing = False
|
||||
self._restart = True
|
||||
|
@ -178,24 +170,51 @@ class CuraEngineBackend(Backend):
|
|||
self._process.terminate()
|
||||
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait())
|
||||
self._process = None
|
||||
#self._createSocket() # Re create the socket
|
||||
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
Logger.log("d", "Exception occured while trying to kill the engine %s", str(e))
|
||||
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
|
||||
|
||||
## Event handler to call when the job to initiate the slicing process is
|
||||
# completed.
|
||||
#
|
||||
# When the start slice job is successfully completed, it will be happily
|
||||
# slicing. This function handles any errors that may occur during the
|
||||
# bootstrapping of a slice job.
|
||||
#
|
||||
# \param job The start slice job that was just finished.
|
||||
def _onStartSliceCompleted(self, job):
|
||||
# Note that cancelled slice jobs can still call this method.
|
||||
if self._start_slice_job is job:
|
||||
self._start_slice_job = None
|
||||
if job.isCancelled() or job.getError() or job.getResult() != True:
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
return
|
||||
else:
|
||||
# Preparation completed, send it to the backend.
|
||||
self._socket.sendMessage(job.getSettingsMessage())
|
||||
self._socket.sendMessage(job.getSliceMessage())
|
||||
|
||||
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
|
||||
return
|
||||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.SettingError:
|
||||
if Application.getInstance().getPlatformActivity:
|
||||
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."), lifetime = 10)
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
return
|
||||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
|
||||
if Application.getInstance().getPlatformActivity:
|
||||
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."), lifetime = 10)
|
||||
self._error_message.show()
|
||||
self.backendStateChange.emit(BackendState.Error)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
return
|
||||
|
||||
# Preparation completed, send it to the backend.
|
||||
self._socket.sendMessage(job.getSliceMessage())
|
||||
|
||||
## Listener for when the scene has changed.
|
||||
#
|
||||
# This should start a slice if the scene is now ready to slice.
|
||||
#
|
||||
# \param source The scene node that was changed.
|
||||
def _onSceneChanged(self, source):
|
||||
if type(source) is not SceneNode:
|
||||
return
|
||||
|
@ -211,6 +230,9 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
self._onChanged()
|
||||
|
||||
## Called when an error occurs in the socket connection towards the engine.
|
||||
#
|
||||
# \param error The exception that occurred.
|
||||
def _onSocketError(self, error):
|
||||
if Application.getInstance().isShuttingDown():
|
||||
return
|
||||
|
@ -221,54 +243,62 @@ class CuraEngineBackend(Backend):
|
|||
if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]:
|
||||
Logger.log("e", "A socket error caused the connection to be reset")
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._onSettingChanged)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._onSettingChanged)
|
||||
## A setting has changed, so check if we must reslice.
|
||||
#
|
||||
# \param instance The setting instance that has changed.
|
||||
# \param property The property of the setting instance that has changed.
|
||||
def _onSettingChanged(self, instance, property):
|
||||
if property == "value": #Only reslice if the value has changed.
|
||||
self._onChanged()
|
||||
|
||||
def _onSettingChanged(self, setting):
|
||||
self._onChanged()
|
||||
|
||||
## Called when a sliced layer data message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing sliced layer data.
|
||||
def _onLayerMessage(self, message):
|
||||
self._stored_layer_data.append(message)
|
||||
|
||||
|
||||
## Called when a progress message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing the slicing progress.
|
||||
def _onProgressMessage(self, message):
|
||||
if self._message:
|
||||
self._message.setProgress(round(message.amount * 100))
|
||||
|
||||
self.processingProgress.emit(message.amount)
|
||||
self.backendStateChange.emit(BackendState.PROCESSING)
|
||||
self.backendStateChange.emit(BackendState.Processing)
|
||||
|
||||
## Called when the engine sends a message that slicing is finished.
|
||||
#
|
||||
# \param message The protobuf message signalling that slicing is finished.
|
||||
def _onSlicingFinishedMessage(self, message):
|
||||
self.backendStateChange.emit(BackendState.DONE)
|
||||
self.backendStateChange.emit(BackendState.Done)
|
||||
self.processingProgress.emit(1.0)
|
||||
|
||||
self._slicing = False
|
||||
|
||||
if self._message:
|
||||
self._message.setProgress(100)
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
|
||||
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
|
||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data)
|
||||
self._process_layers_job.start()
|
||||
self._stored_layer_data = []
|
||||
|
||||
## Called when a g-code message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing g-code, encoded as UTF-8.
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||
|
||||
## Called when a g-code prefix message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing the g-code prefix,
|
||||
# encoded as UTF-8.
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||
|
||||
## Called when a print time message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing the print time and
|
||||
# material amount.
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
|
||||
## Creates a new socket connection.
|
||||
def _createSocket(self):
|
||||
super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto")))
|
||||
|
||||
|
@ -276,28 +306,41 @@ class CuraEngineBackend(Backend):
|
|||
def forceSlice(self):
|
||||
self._change_timer.start()
|
||||
|
||||
def _onChanged(self):
|
||||
if not self._profile:
|
||||
return
|
||||
|
||||
## Called when anything has changed to the stuff that needs to be sliced.
|
||||
#
|
||||
# This indicates that we should probably re-slice soon.
|
||||
def _onChanged(self, *args, **kwargs):
|
||||
self._change_timer.start()
|
||||
|
||||
## Called when the back-end connects to the front-end.
|
||||
def _onBackendConnected(self):
|
||||
if self._restart:
|
||||
self._onChanged()
|
||||
self._restart = False
|
||||
|
||||
## Called when the user starts using some tool.
|
||||
#
|
||||
# When the user starts using a tool, we should pause slicing to prevent
|
||||
# continuously slicing while the user is dragging some tool handle.
|
||||
#
|
||||
# \param tool The tool that the user is using.
|
||||
def _onToolOperationStarted(self, tool):
|
||||
self._terminate() # Do not continue slicing once a tool has started
|
||||
self._enabled = False # Do not reslice when a tool is doing it's 'thing'
|
||||
|
||||
## Called when the user stops using some tool.
|
||||
#
|
||||
# This indicates that we can safely start slicing again.
|
||||
#
|
||||
# \param tool The tool that the user was using.
|
||||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True # Tool stop, start listening for changes again.
|
||||
|
||||
## Called when the user changes the active view mode.
|
||||
def _onActiveViewChanged(self):
|
||||
if Application.getInstance().getController().getActiveView():
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
if view.getPluginId() == "LayerView": #If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||
self._layer_view_active = True
|
||||
# There is data and we're not slicing at the moment
|
||||
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
||||
|
@ -308,11 +351,36 @@ class CuraEngineBackend(Backend):
|
|||
else:
|
||||
self._layer_view_active = False
|
||||
|
||||
def _onInstanceChanged(self):
|
||||
self._terminate()
|
||||
|
||||
## Called when the back-end self-terminates.
|
||||
#
|
||||
# We should reset our state and start listening for new connections.
|
||||
def _onBackendQuit(self):
|
||||
if not self._restart and self._process:
|
||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
|
||||
self._process = None
|
||||
self._createSocket()
|
||||
|
||||
## Called when the global container stack changes
|
||||
def _onGlobalStackChanged(self):
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
self._global_container_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) #Note: Only starts slicing when the value changed.
|
||||
self._global_container_stack.containersChanged.connect(self._onChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
self._onChanged()
|
||||
|
||||
def _onActiveExtruderChanged(self):
|
||||
if self._active_extruder_stack:
|
||||
self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
self._active_extruder_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
||||
self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_extruder_stack:
|
||||
self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
|
||||
self._active_extruder_stack.containersChanged.connect(self._onChanged)
|
||||
self._onChanged()
|
|
@ -62,8 +62,6 @@ class ProcessSlicedLayersJob(Job):
|
|||
self._progress.hide()
|
||||
return
|
||||
|
||||
settings = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
mesh = MeshData()
|
||||
layer_data = LayerDataBuilder.LayerDataBuilder()
|
||||
layer_count = len(self._layers)
|
||||
|
@ -105,7 +103,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
Job.yieldThread()
|
||||
Job.yieldThread()
|
||||
current_layer += 1
|
||||
progress = (current_layer / layer_count) * 100
|
||||
progress = (current_layer / layer_count) * 99
|
||||
# TODO: Rebuild the layer data mesh once the layer has been processed.
|
||||
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
|
||||
|
||||
|
@ -132,8 +130,9 @@ class ProcessSlicedLayersJob(Job):
|
|||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot()) # Note: After this we can no longer abort!
|
||||
|
||||
if not settings.getSettingValue("machine_center_is_zero"):
|
||||
new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2))
|
||||
settings = Application.getInstance().getGlobalContainerStack()
|
||||
if not settings.getProperty("machine_center_is_zero", "value"):
|
||||
new_node.setPosition(Vector(-settings.getProperty("machine_width", "value") / 2, 0.0, settings.getProperty("machine_depth", "value") / 2))
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(100)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import numpy
|
||||
from string import Formatter
|
||||
import traceback
|
||||
from enum import IntEnum
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
|
@ -12,8 +12,16 @@ from UM.Logger import Logger
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from cura.ExtruderManager import ExtruderManager
|
||||
|
||||
class StartJobResult(IntEnum):
|
||||
Finished = 1
|
||||
Error = 2
|
||||
SettingError = 3
|
||||
NothingToSlice = 4
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcod
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
|
@ -30,33 +38,65 @@ class GcodeStartEndFormatter(Formatter):
|
|||
|
||||
## Job class that builds up the message of scene data to send to CuraEngine.
|
||||
class StartSliceJob(Job):
|
||||
def __init__(self, profile, slice_message, settings_message):
|
||||
def __init__(self, slice_message):
|
||||
super().__init__()
|
||||
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._profile = profile
|
||||
self._slice_message = slice_message
|
||||
self._settings_message = settings_message
|
||||
self._is_cancelled = False
|
||||
|
||||
def getSettingsMessage(self):
|
||||
return self._settings_message
|
||||
|
||||
def getSliceMessage(self):
|
||||
return self._slice_message
|
||||
|
||||
## Check if a stack has any errors.
|
||||
## returns true if it has errors, false otherwise.
|
||||
def _checkStackForErrors(self, stack):
|
||||
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 %s is not valid, but %s. Aborting slicing.", key, validation_state)
|
||||
return True
|
||||
Job.yieldThread()
|
||||
return False
|
||||
|
||||
## Runs the job that initiates the slicing.
|
||||
def run(self):
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not stack:
|
||||
self.setResult(StartJobResult.Error)
|
||||
return
|
||||
|
||||
# Don't slice if there is a setting with an error value.
|
||||
if self._checkStackForErrors(stack):
|
||||
self.setResult(StartJobResult.SettingError)
|
||||
return
|
||||
|
||||
# Don't slice if there is a per object setting with an error value.
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is not SceneNode or not node.isSelectable():
|
||||
continue
|
||||
|
||||
if self._checkStackForErrors(node.callDecoration("getStack")):
|
||||
self.setResult(StartJobResult.SettingError)
|
||||
return
|
||||
|
||||
with self._scene.getSceneLock():
|
||||
# Remove old layer data.
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("getLayerData"):
|
||||
node.getParent().removeChild(node)
|
||||
break
|
||||
|
||||
# Get the objects in their groups to print.
|
||||
object_groups = []
|
||||
if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
|
||||
if stack.getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
for node in OneAtATimeIterator(self._scene.getRoot()):
|
||||
temp_list = []
|
||||
|
||||
# Node can't be printed, so don't bother sending it.
|
||||
if getattr(node, "_outside_buildarea", False):
|
||||
continue
|
||||
|
||||
|
@ -83,9 +123,13 @@ class StartSliceJob(Job):
|
|||
object_groups.append(temp_list)
|
||||
|
||||
if not object_groups:
|
||||
self.setResult(StartJobResult.NothingToSlice)
|
||||
return
|
||||
|
||||
self._buildSettingsMessage(self._profile)
|
||||
self._buildGlobalSettingsMessage(stack)
|
||||
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()):
|
||||
self._buildExtruderMessage(extruder_stack)
|
||||
|
||||
for group in object_groups:
|
||||
group_message = self._slice_message.addRepeatedMessage("object_lists")
|
||||
|
@ -97,8 +141,10 @@ class StartSliceJob(Job):
|
|||
obj = group_message.addRepeatedMessage("objects")
|
||||
obj.id = id(object)
|
||||
verts = numpy.array(mesh_data.getVertices())
|
||||
verts[:,[1,2]] = verts[:,[2,1]]
|
||||
verts[:,1] *= -1
|
||||
|
||||
# Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
|
||||
verts[:, [1, 2]] = verts[:, [2, 1]]
|
||||
verts[:, 1] *= -1
|
||||
|
||||
obj.vertices = verts
|
||||
|
||||
|
@ -106,7 +152,7 @@ class StartSliceJob(Job):
|
|||
|
||||
Job.yieldThread()
|
||||
|
||||
self.setResult(True)
|
||||
self.setResult(StartJobResult.Finished)
|
||||
|
||||
def cancel(self):
|
||||
super().cancel()
|
||||
|
@ -121,38 +167,45 @@ class StartSliceJob(Job):
|
|||
fmt = GcodeStartEndFormatter()
|
||||
return str(fmt.format(value, **settings)).encode("utf-8")
|
||||
except:
|
||||
Logger.log("w", "Unabled to do token replacement on start/end gcode %s", traceback.format_exc())
|
||||
Logger.logException("w", "Unable to do token replacement on start/end gcode")
|
||||
return str(value).encode("utf-8")
|
||||
|
||||
def _buildSettingsMessage(self, profile):
|
||||
settings = profile.getAllSettingValues(include_machine = True)
|
||||
def _buildExtruderMessage(self, stack):
|
||||
message = self._slice_message.addRepeatedMessage("extruders")
|
||||
message.id = int(stack.getMetaDataEntry("position"))
|
||||
for key in stack.getAllKeys():
|
||||
setting = message.getMessage("settings").addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
|
||||
Job.yieldThread()
|
||||
|
||||
## Sends all global settings to the engine.
|
||||
#
|
||||
# The settings are taken from the global stack. This does not include any
|
||||
# per-extruder settings or per-object settings.
|
||||
def _buildGlobalSettingsMessage(self, stack):
|
||||
keys = stack.getAllKeys()
|
||||
settings = {}
|
||||
for key in keys:
|
||||
settings[key] = stack.getProperty(key, "value")
|
||||
|
||||
start_gcode = settings["machine_start_gcode"]
|
||||
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode
|
||||
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode #Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
|
||||
settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode
|
||||
for key, value in settings.items():
|
||||
s = self._settings_message.addRepeatedMessage("settings")
|
||||
s.name = key
|
||||
if key == "machine_start_gcode" or key == "machine_end_gcode":
|
||||
s.value = self._expandGcodeTokens(key, value, settings)
|
||||
|
||||
for key, value in settings.items(): #Add all submessages for each individual setting.
|
||||
setting_message = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings")
|
||||
setting_message.name = key
|
||||
if key == "machine_start_gcode" or key == "machine_end_gcode": #If it's a g-code message, use special formatting.
|
||||
setting_message.value = self._expandGcodeTokens(key, value, settings)
|
||||
else:
|
||||
s.value = str(value).encode("utf-8")
|
||||
setting_message.value = str(value).encode("utf-8")
|
||||
|
||||
def _handlePerObjectSettings(self, node, message):
|
||||
profile = node.callDecoration("getProfile")
|
||||
if profile:
|
||||
for key, value in profile.getAllSettingValues().items():
|
||||
stack = node.callDecoration("getStack")
|
||||
if stack:
|
||||
for key in stack.getAllKeys():
|
||||
setting = message.addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(value).encode()
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
object_settings = node.callDecoration("getAllSettingValues")
|
||||
if not object_settings:
|
||||
return
|
||||
for key, value in object_settings.items():
|
||||
setting = message.addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(value).encode()
|
||||
|
||||
Job.yieldThread()
|
||||
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
|
||||
Job.yieldThread()
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"name": catalog.i18nc("@label", "CuraEngine Backend"),
|
||||
"author": "Ultimaker",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides the link to the CuraEngine slicing backend."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os.path
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
|
||||
from cura.ProfileReader import ProfileReader
|
||||
|
||||
## A plugin that reads profile data from Cura profile files.
|
||||
#
|
||||
|
@ -25,17 +26,17 @@ class CuraProfileReader(ProfileReader):
|
|||
# returned.
|
||||
def read(self, file_name):
|
||||
# Create an empty profile.
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
|
||||
serialised = ""
|
||||
profile = InstanceContainer(os.path.basename(os.path.splitext(file_name)[0]))
|
||||
profile.addMetaDataEntry("type", "quality")
|
||||
try:
|
||||
with open(file_name) as f: # Open file for reading.
|
||||
serialised = f.read()
|
||||
serialized = f.read()
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
profile.deserialize(serialized)
|
||||
except Exception as e: # Parsing error. This is not a (valid) Cura profile then.
|
||||
Logger.log("e", "Error while trying to parse profile: %s", str(e))
|
||||
return None
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing Cura profiles."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
from UM.Logger import Logger
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Settings.ProfileWriter import ProfileWriter
|
||||
from cura.ProfileWriter import ProfileWriter
|
||||
|
||||
|
||||
## Writes profiles to Cura's own profile format with config files.
|
||||
|
@ -16,10 +16,10 @@ class CuraProfileWriter(ProfileWriter):
|
|||
# \return \code True \endcode if the writing was successful, or \code
|
||||
# False \endcode if it wasn't.
|
||||
def write(self, path, profile):
|
||||
serialised = profile.serialise()
|
||||
serialized = profile.serialize()
|
||||
try:
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f: # Open the specified file.
|
||||
f.write(serialised)
|
||||
f.write(serialized)
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to write profile to %s: %s", path, str(e))
|
||||
return False
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for exporting Cura profiles."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"profile_writer": [
|
||||
{
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Logger import Logger
|
||||
from UM.Application import Application
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To create a complete setting profile to store in the g-code.
|
||||
import re #For escaping characters in the settings.
|
||||
import copy
|
||||
|
||||
|
||||
## Writes g-code to a file.
|
||||
#
|
||||
# While this poses as a mesh writer, what this really does is take the g-code
|
||||
# in the entire scene and write it to an output device. Since the g-code of a
|
||||
# single mesh isn't separable from the rest what with rafts and travel moves
|
||||
# and all, it doesn't make sense to write just a single mesh.
|
||||
#
|
||||
# So this plug-in takes the g-code that is stored in the root of the scene
|
||||
# node tree, adds a bit of extra information about the profiles and writes
|
||||
# that to the output device.
|
||||
class GCodeWriter(MeshWriter):
|
||||
## The file format version of the serialised g-code.
|
||||
#
|
||||
|
@ -32,7 +41,7 @@ class GCodeWriter(MeshWriter):
|
|||
|
||||
def write(self, stream, node, mode = MeshWriter.OutputMode.TextMode):
|
||||
if mode != MeshWriter.OutputMode.TextMode:
|
||||
Logger.log("e", "GCode Writer does not support non-text mode")
|
||||
Logger.log("e", "GCode Writer does not support non-text mode.")
|
||||
return False
|
||||
|
||||
scene = Application.getInstance().getController().getScene()
|
||||
|
@ -40,26 +49,30 @@ class GCodeWriter(MeshWriter):
|
|||
if gcode_list:
|
||||
for gcode in gcode_list:
|
||||
stream.write(gcode)
|
||||
# Serialise the profile and put them at the end of the file.
|
||||
profile = self._serialiseProfile(Application.getInstance().getMachineManager().getWorkingProfile())
|
||||
stream.write(profile)
|
||||
# Serialise the current container stack and put it at the end of the file.
|
||||
settings = self._serialiseSettings(Application.getInstance().getGlobalContainerStack())
|
||||
stream.write(settings)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
## Serialises the profile to prepare it for saving in the g-code.
|
||||
## Serialises a container stack to prepare it for writing at the end of the
|
||||
# g-code.
|
||||
#
|
||||
# The profile are serialised, and special characters (including newline)
|
||||
# The settings are serialised, and special characters (including newline)
|
||||
# are escaped.
|
||||
#
|
||||
# \param profile The profile to serialise.
|
||||
# \return A serialised string of the profile.
|
||||
def _serialiseProfile(self, profile):
|
||||
# \param settings A container stack to serialise.
|
||||
# \return A serialised string of the settings.
|
||||
def _serialiseSettings(self, settings):
|
||||
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
|
||||
prefix_length = len(prefix)
|
||||
|
||||
# Serialise a deepcopy to remove the defaults from the profile
|
||||
serialised = copy.deepcopy(profile).serialise()
|
||||
all_settings = InstanceContainer("G-code-imported-profile") #Create a new 'profile' with ALL settings so that the slice can be precisely reproduced.
|
||||
all_settings.setDefinition(settings.getBottom())
|
||||
for key in settings.getAllKeys():
|
||||
all_settings.setProperty(key, "value", settings.getProperty(key, "value")) #Just copy everything over to the setting instance.
|
||||
serialised = all_settings.serialize()
|
||||
|
||||
# Escape characters that have a special meaning in g-code comments.
|
||||
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Writes GCode to a file."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
|
||||
"mesh_writer": {
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Enables ability to generate printable geometry from 2D image files."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"mesh_reader": [
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@ from UM.Scene.Selection import Selection
|
|||
from UM.Math.Color import Color
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
@ -41,7 +42,10 @@ class LayerView(View):
|
|||
self._top_layers_job = None
|
||||
self._activity = False
|
||||
|
||||
self._solid_layers = 5
|
||||
Preferences.getInstance().addPreference("view/top_layer_count", 1)
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||
|
||||
self._top_layer_timer = QTimer()
|
||||
self._top_layer_timer.setInterval(50)
|
||||
|
@ -125,8 +129,7 @@ class LayerView(View):
|
|||
if self._current_layer_num > self._max_layers:
|
||||
self._current_layer_num = self._max_layers
|
||||
|
||||
self._current_layer_mesh = None
|
||||
self._current_layer_jumps = None
|
||||
self.resetLayerData()
|
||||
|
||||
self._top_layer_timer.start()
|
||||
|
||||
|
@ -209,6 +212,15 @@ class LayerView(View):
|
|||
|
||||
self._top_layers_job = None
|
||||
|
||||
def _onPreferencesChanged(self, preference):
|
||||
if preference != "view/top_layer_count":
|
||||
return
|
||||
|
||||
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||
|
||||
self.resetLayerData()
|
||||
self._top_layer_timer.start()
|
||||
|
||||
class _CreateTopLayersJob(Job):
|
||||
def __init__(self, scene, layer_number, solid_layers):
|
||||
super().__init__()
|
||||
|
|
|
@ -14,7 +14,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides the Layer view."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"view": {
|
||||
"name": catalog.i18nc("@item:inlistbox", "Layers"),
|
||||
|
|
|
@ -50,7 +50,8 @@
|
|||
"skirt_minimal_length": "skirt_minimal_length",
|
||||
"brim_line_count": "brim_line_count",
|
||||
"raft_margin": "raft_margin",
|
||||
"raft_airgap": "raft_airgap_all",
|
||||
"raft_airgap": "float(raft_airgap_all) + float(raft_airgap)",
|
||||
"layer_0_z_overlap": "raft_airgap",
|
||||
"raft_surface_layers": "raft_surface_layers",
|
||||
"raft_surface_thickness": "raft_surface_thickness",
|
||||
"raft_surface_line_width": "raft_surface_linewidth",
|
||||
|
|
|
@ -9,8 +9,9 @@ import os.path #For concatenating the path to the plugin and the relative path t
|
|||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Logger import Logger #Logging errors.
|
||||
from UM.PluginRegistry import PluginRegistry #For getting the path to this plugin's directory.
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #For getting the current machine's defaults.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
|
||||
from cura.ProfileReader import ProfileReader #The plug-in type to implement.
|
||||
|
||||
## A plugin that reads profile data from legacy Cura versions.
|
||||
#
|
||||
|
@ -66,7 +67,7 @@ class LegacyProfileReader(ProfileReader):
|
|||
if file_name.split(".")[-1] != "ini":
|
||||
return None
|
||||
Logger.log("i", "Importing legacy profile from file " + file_name + ".")
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile.
|
||||
profile = InstanceContainer("Imported Legacy Profile") #Create an empty profile.
|
||||
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
try:
|
||||
|
@ -103,23 +104,24 @@ class LegacyProfileReader(ProfileReader):
|
|||
if "target_version" not in dict_of_doom:
|
||||
Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?")
|
||||
return None
|
||||
if Profile.ProfileVersion != dict_of_doom["target_version"]:
|
||||
Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the profile version (version %s)!", dict_of_doom["target_version"], str(Profile.ProfileVersion))
|
||||
if InstanceContainer.Version != dict_of_doom["target_version"]:
|
||||
Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version))
|
||||
return None
|
||||
|
||||
if "translation" not in dict_of_doom:
|
||||
Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
|
||||
return None
|
||||
current_printer = Application.getInstance().getGlobalContainerStack().findContainer({ }, DefinitionContainer)
|
||||
for new_setting in dict_of_doom["translation"]: #Evaluate all new settings that would get a value from the translations.
|
||||
old_setting_expression = dict_of_doom["translation"][new_setting]
|
||||
compiled = compile(old_setting_expression, new_setting, "eval")
|
||||
try:
|
||||
new_value = eval(compiled, {"math": math}, legacy_settings) #Pass the legacy settings as local variables to allow access to in the evaluation.
|
||||
value_using_defaults = eval(compiled, {"math": math}, defaults) #Evaluate again using only the default values to try to see if they are default.
|
||||
except Exception as e: #Probably some setting name that was missing or something else that went wrong in the ini file.
|
||||
except Exception: #Probably some setting name that was missing or something else that went wrong in the ini file.
|
||||
Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
|
||||
continue
|
||||
if new_value != value_using_defaults and profile.getSettingValue(new_setting) != new_value: #Not equal to the default in the new Cura OR the default in the legacy Cura.
|
||||
if new_value != value_using_defaults and current_printer.findDefinitions(key = new_setting).default_value != new_value: #Not equal to the default in the new Cura OR the default in the legacy Cura.
|
||||
profile.setSettingValue(new_setting, new_value) #Store the setting in the profile!
|
||||
|
||||
if len(profile.getChangedSettings()) == 0:
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing profiles from legacy Cura versions."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
|
|
29
plugins/PerObjectSettingsTool/PerObjectCategory.qml
Normal file
29
plugins/PerObjectSettingsTool/PerObjectCategory.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
import ".."
|
||||
|
||||
Button {
|
||||
id: base;
|
||||
|
||||
style: UM.Theme.styles.sidebar_category;
|
||||
|
||||
signal showTooltip(string text);
|
||||
signal hideTooltip();
|
||||
signal contextMenuRequested()
|
||||
|
||||
text: definition.label
|
||||
iconSource: UM.Theme.getIcon(definition.icon)
|
||||
|
||||
checkable: true
|
||||
checked: definition.expanded
|
||||
|
||||
onClicked: definition.expanded ? settingDefinitionsModel.collapse(definition.key) : settingDefinitionsModel.expandAll(definition.key)
|
||||
}
|
34
plugins/PerObjectSettingsTool/PerObjectItem.qml
Normal file
34
plugins/PerObjectSettingsTool/PerObjectItem.qml
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
x: model.depth * UM.Theme.getSize("default_margin").width;
|
||||
text: model.description;
|
||||
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
|
||||
Button
|
||||
{
|
||||
id: check
|
||||
|
||||
text: definition.label
|
||||
|
||||
onClicked:
|
||||
{
|
||||
addedSettingsModel.setVisible(model.key, true);
|
||||
settingPickDialog.visible = false
|
||||
UM.ActiveTool.forceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
from UM.Application import Application
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Logger import Logger
|
||||
|
||||
from cura.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container
|
||||
# are returned as visible.
|
||||
class PerObjectSettingVisibilityHandler(QObject):
|
||||
def __init__(self, parent = None, *args, **kwargs):
|
||||
super().__init__(parent = parent, *args, **kwargs)
|
||||
self._selected_object_id = None
|
||||
|
||||
visibilityChanged = pyqtSignal()
|
||||
|
||||
def setSelectedObjectId(self, id):
|
||||
self._selected_object_id = id
|
||||
self.visibilityChanged.emit()
|
||||
|
||||
@pyqtProperty("quint64", fset = setSelectedObjectId)
|
||||
def selectedObjectId(self):
|
||||
pass
|
||||
|
||||
def setVisible(self, visible):
|
||||
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
|
||||
if not node:
|
||||
return
|
||||
stack = node.callDecoration("getStack")
|
||||
if not stack:
|
||||
node.addDecorator(SettingOverrideDecorator())
|
||||
stack = node.callDecoration("getStack")
|
||||
|
||||
settings = stack.getTop()
|
||||
all_instances = settings.findInstances(**{})
|
||||
visibility_changed = False # Flag to check if at the end the signal needs to be emitted
|
||||
|
||||
# Remove all instances that are not in visibility list
|
||||
for instance in all_instances:
|
||||
if instance.definition.key not in visible:
|
||||
settings.removeInstance(instance.definition.key)
|
||||
visibility_changed = True
|
||||
|
||||
# Add all instances that are not added, but are in visiblity list
|
||||
for item in visible:
|
||||
if not settings.getInstance(item):
|
||||
definition_container = Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
definitions = definition_container.findDefinitions(key = item)
|
||||
if definitions:
|
||||
settings.addInstance(SettingInstance(definitions[0], settings))
|
||||
visibility_changed = True
|
||||
else:
|
||||
Logger.log("w", "Unable to add instance (%s) to perobject visibility because we couldn't find the matching definition", item)
|
||||
|
||||
if visibility_changed:
|
||||
self.visibilityChanged.emit()
|
||||
|
||||
def getVisible(self):
|
||||
visible_settings = set()
|
||||
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
|
||||
if not node:
|
||||
return visible_settings
|
||||
|
||||
stack = node.callDecoration("getStack")
|
||||
if not stack:
|
||||
return visible_settings
|
||||
|
||||
settings = stack.getTop()
|
||||
if not settings:
|
||||
return visible_settings
|
||||
|
||||
all_instances = settings.findInstances(**{})
|
||||
for instance in all_instances:
|
||||
visible_settings.add(instance.definition.key)
|
||||
return visible_settings
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from UM.Settings.ProfileOverrideDecorator import ProfileOverrideDecorator
|
||||
|
||||
from . import SettingOverrideModel
|
||||
|
||||
class PerObjectSettingsModel(ListModel):
|
||||
IdRole = Qt.UserRole + 1
|
||||
XRole = Qt.UserRole + 2
|
||||
YRole = Qt.UserRole + 3
|
||||
MaterialRole = Qt.UserRole + 4
|
||||
ProfileRole = Qt.UserRole + 5
|
||||
SettingsRole = Qt.UserRole + 6
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._root = self._scene.getRoot()
|
||||
self.addRoleName(self.IdRole,"id")
|
||||
self.addRoleName(self.MaterialRole, "material")
|
||||
self.addRoleName(self.ProfileRole, "profile")
|
||||
self.addRoleName(self.SettingsRole, "settings")
|
||||
self._updateModel()
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def setObjectProfile(self, object_id, profile_name):
|
||||
self.setProperty(self.find("id", object_id), "profile", profile_name)
|
||||
|
||||
profile = None
|
||||
if profile_name != "global":
|
||||
profile = Application.getInstance().getMachineManager().findProfile(profile_name)
|
||||
|
||||
node = self._scene.findObject(object_id)
|
||||
if profile:
|
||||
if not node.getDecorator(ProfileOverrideDecorator):
|
||||
node.addDecorator(ProfileOverrideDecorator())
|
||||
node.callDecoration("setProfile", profile)
|
||||
else:
|
||||
if node.getDecorator(ProfileOverrideDecorator):
|
||||
node.removeDecorator(ProfileOverrideDecorator)
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def addSettingOverride(self, object_id, key):
|
||||
machine = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
if not machine:
|
||||
return
|
||||
|
||||
node = self._scene.findObject(object_id)
|
||||
if not node.getDecorator(SettingOverrideDecorator):
|
||||
node.addDecorator(SettingOverrideDecorator())
|
||||
|
||||
node.callDecoration("addSetting", key)
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def removeSettingOverride(self, object_id, key):
|
||||
node = self._scene.findObject(object_id)
|
||||
node.callDecoration("removeSetting", key)
|
||||
|
||||
if len(node.callDecoration("getAllSettings")) == 0:
|
||||
node.removeDecorator(SettingOverrideDecorator)
|
||||
|
||||
def _updateModel(self):
|
||||
self.clear()
|
||||
for node in BreadthFirstIterator(self._root):
|
||||
if type(node) is not SceneNode or not node.isSelectable():
|
||||
continue
|
||||
node_profile = node.callDecoration("getProfile")
|
||||
if not node_profile:
|
||||
node_profile = "global"
|
||||
else:
|
||||
node_profile = node_profile.getName()
|
||||
|
||||
self.appendItem({
|
||||
"id": id(node),
|
||||
"material": "",
|
||||
"profile": node_profile,
|
||||
"settings": SettingOverrideModel.SettingOverrideModel(node)
|
||||
})
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
|
@ -6,83 +6,231 @@ import QtQuick.Controls 1.2
|
|||
import QtQuick.Controls.Styles 1.2
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import UM 1.1 as UM
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
import ".."
|
||||
|
||||
Item {
|
||||
id: base;
|
||||
property int currentIndex: UM.ActiveTool.properties.getValue("SelectedIndex")
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
|
||||
Column {
|
||||
Column
|
||||
{
|
||||
id: items
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height;
|
||||
|
||||
Column {
|
||||
id: customisedSettings
|
||||
spacing: UM.Theme.getSize("default_lining").height;
|
||||
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("setting").height/2;
|
||||
Row
|
||||
{
|
||||
ComboBox
|
||||
{
|
||||
id: extruderSelector
|
||||
|
||||
Repeater {
|
||||
id: settings;
|
||||
model: Cura.ExtrudersModel
|
||||
{
|
||||
id: extruders_model
|
||||
onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1
|
||||
onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1
|
||||
}
|
||||
visible: extruders_model.rowCount() > 1
|
||||
textRole: "name"
|
||||
width: items.width
|
||||
height: UM.Theme.getSize("section").height
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: wheel.accepted = true;
|
||||
}
|
||||
|
||||
model: UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).settings
|
||||
style: ComboBoxStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(extruderSelector.hovered || base.activeFocus)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_highlight");
|
||||
}
|
||||
else
|
||||
{
|
||||
return UM.Theme.getColor("setting_control");
|
||||
}
|
||||
}
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
label: Item
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
height: UM.Theme.getSize("setting_control").height / 2
|
||||
width: height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
UM.SettingItem {
|
||||
color: extruders_model.getItem(extruderSelector.currentIndex).colour
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
Label
|
||||
{
|
||||
anchors.left: swatch.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.right: downArrow.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: extruderSelector.currentText
|
||||
font: UM.Theme.getFont("default")
|
||||
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
|
||||
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width + 5
|
||||
sourceSize.height: width + 5
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id);
|
||||
onModelChanged: updateCurrentIndex();
|
||||
|
||||
function updateCurrentIndex()
|
||||
{
|
||||
for(var i = 0; i < extruders_model.rowCount(); ++i)
|
||||
{
|
||||
if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder"))
|
||||
{
|
||||
extruderSelector.currentIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
extruderSelector.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
id: contents
|
||||
height: childrenRect.height;
|
||||
|
||||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
|
||||
{
|
||||
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Row
|
||||
{
|
||||
Loader
|
||||
{
|
||||
id: settingLoader
|
||||
width: UM.Theme.getSize("setting").width;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("section").height;
|
||||
|
||||
name: model.label;
|
||||
type: model.type;
|
||||
value: model.value;
|
||||
description: model.description;
|
||||
unit: model.unit;
|
||||
valid: model.valid;
|
||||
visible: !model.global_only
|
||||
options: model.options
|
||||
indent: false
|
||||
property var definition: model
|
||||
property var settingDefinitionsModel: addedSettingsModel
|
||||
property var propertyProvider: provider
|
||||
|
||||
style: UM.Theme.styles.setting_item;
|
||||
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
|
||||
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
|
||||
//causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
|
||||
asynchronous: model.type != "enum" && model.type != "extruder"
|
||||
|
||||
onItemValueChanged: {
|
||||
settings.model.setSettingValue(model.key, value)
|
||||
onLoaded: {
|
||||
settingLoader.item.showRevertButton = false
|
||||
settingLoader.item.showInheritButton = false
|
||||
settingLoader.item.doDepthIndentation = false
|
||||
}
|
||||
|
||||
Button
|
||||
sourceComponent:
|
||||
{
|
||||
anchors.left: parent.right;
|
||||
|
||||
width: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
onClicked: UM.ActiveTool.properties.getValue("Model").removeSettingOverride(UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).id, model.key)
|
||||
|
||||
style: ButtonStyle
|
||||
switch(model.type)
|
||||
{
|
||||
background: Rectangle
|
||||
case "int":
|
||||
return settingTextField
|
||||
case "float":
|
||||
return settingTextField
|
||||
case "enum":
|
||||
return settingComboBox
|
||||
case "extruder":
|
||||
return settingExtruder
|
||||
case "bool":
|
||||
return settingCheckBox
|
||||
case "str":
|
||||
return settingTextField
|
||||
case "category":
|
||||
return settingCategory
|
||||
default:
|
||||
return settingUnknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
width: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
onClicked: addedSettingsModel.setVisible(model.key, false);
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor;
|
||||
UM.RecolorImage
|
||||
{
|
||||
color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor;
|
||||
UM.RecolorImage
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width/2
|
||||
height: parent.height/2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("cross1")
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width/2
|
||||
height: parent.height/2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("cross1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: provider
|
||||
|
||||
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
|
||||
key: model.key
|
||||
watchedProperties: [ "value", "enabled", "validationState" ]
|
||||
storeIndex: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,6 +281,7 @@ Item {
|
|||
id: settingPickDialog
|
||||
|
||||
title: catalog.i18nc("@title:window", "Pick a Setting to Customize")
|
||||
property string labelFilter: ""
|
||||
|
||||
TextField {
|
||||
id: filter;
|
||||
|
@ -145,123 +294,62 @@ Item {
|
|||
|
||||
placeholderText: catalog.i18nc("@label:textbox", "Filter...");
|
||||
|
||||
onTextChanged: settingCategoriesModel.filter(text);
|
||||
onTextChanged:
|
||||
{
|
||||
if(text != "")
|
||||
{
|
||||
listview.model.filter = {"settable_per_mesh": true, "label": "*" + text}
|
||||
}
|
||||
else
|
||||
{
|
||||
listview.model.filter = {"settable_per_mesh": true}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: view;
|
||||
anchors {
|
||||
ScrollView
|
||||
{
|
||||
id: scrollView
|
||||
|
||||
anchors
|
||||
{
|
||||
top: filter.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
ListView
|
||||
{
|
||||
id:listview
|
||||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
id: definitionsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
filter:
|
||||
{
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
|
||||
}
|
||||
delegate:Loader
|
||||
{
|
||||
id: loader
|
||||
|
||||
Column {
|
||||
width: view.width - UM.Theme.getSize("default_margin").width * 2;
|
||||
height: childrenRect.height;
|
||||
width: parent.width
|
||||
height: model.type != undefined ? UM.Theme.getSize("section").height : 0;
|
||||
|
||||
Repeater {
|
||||
id: settingList;
|
||||
property var definition: model
|
||||
property var settingDefinitionsModel: definitionsModel
|
||||
|
||||
model: UM.SettingCategoriesModel { id: settingCategoriesModel; }
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem;
|
||||
|
||||
width: parent.width;
|
||||
height: childrenRect.height;
|
||||
visible: model.visible && settingsColumn.childrenHeight != 0 //If all children are hidden, the height is 0, and then the category header must also be hidden.
|
||||
|
||||
ToolButton {
|
||||
id: categoryHeader;
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
width: parent.width;
|
||||
onCheckedChanged: settingsColumn.state != "" ? settingsColumn.state = "" : settingsColumn.state = "collapsed";
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Rectangle
|
||||
{
|
||||
width: control.width;
|
||||
height: control.height;
|
||||
color: control.hovered ? palette.highlight : "transparent";
|
||||
}
|
||||
label: Row
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").width;
|
||||
Image
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
source: control.checked ? UM.Theme.getIcon("arrow_right") : UM.Theme.getIcon("arrow_bottom");
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: control.text;
|
||||
font.bold: true;
|
||||
color: control.hovered ? palette.highlightedText : palette.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property variant settingsModel: model.settings;
|
||||
|
||||
Column {
|
||||
id: settingsColumn;
|
||||
|
||||
anchors.top: categoryHeader.bottom;
|
||||
|
||||
property real childrenHeight:
|
||||
{
|
||||
var h = 0.0;
|
||||
for(var i in children)
|
||||
{
|
||||
var item = children[i];
|
||||
h += children[i].height;
|
||||
if(item.settingVisible)
|
||||
{
|
||||
if(i > 0)
|
||||
{
|
||||
h += spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
width: childrenRect.width;
|
||||
height: childrenHeight;
|
||||
Repeater {
|
||||
model: delegateItem.settingsModel;
|
||||
|
||||
delegate: ToolButton {
|
||||
id: button;
|
||||
x: model.visible_depth * UM.Theme.getSize("default_margin").width;
|
||||
text: model.name;
|
||||
tooltip: model.description;
|
||||
visible: !model.global_only
|
||||
height: model.global_only ? 0 : undefined
|
||||
|
||||
onClicked: {
|
||||
var object_id = UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).id;
|
||||
UM.ActiveTool.properties.getValue("Model").addSettingOverride(object_id, model.key);
|
||||
settingPickDialog.visible = false;
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "filtered"
|
||||
when: model.filtered || !model.visible || !model.enabled
|
||||
PropertyChanges { target: button; height: 0; opacity: 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "collapsed";
|
||||
|
||||
PropertyChanges { target: settingsColumn; opacity: 0; height: 0; }
|
||||
}
|
||||
asynchronous: true
|
||||
source:
|
||||
{
|
||||
switch(model.type)
|
||||
{
|
||||
case "category":
|
||||
return "PerObjectCategory.qml"
|
||||
default:
|
||||
return "PerObjectItem.qml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,4 +367,46 @@ Item {
|
|||
}
|
||||
|
||||
SystemPalette { id: palette; }
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingTextField;
|
||||
|
||||
Cura.SettingTextField { }
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingComboBox;
|
||||
|
||||
Cura.SettingComboBox { }
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingExtruder;
|
||||
|
||||
Cura.SettingExtruder { }
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingCheckBox;
|
||||
|
||||
Cura.SettingCheckBox { }
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingCategory;
|
||||
|
||||
Cura.SettingCategory { }
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: settingUnknown;
|
||||
|
||||
Cura.SettingUnknown { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,57 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Tool import Tool
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from cura.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
from . import PerObjectSettingsModel
|
||||
|
||||
## 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.
|
||||
class PerObjectSettingsTool(Tool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._model = None
|
||||
|
||||
self.setExposedProperties("Model", "SelectedIndex")
|
||||
self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder")
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
Selection.selectionChanged.connect(self.propertyChanged)
|
||||
self._onPreferenceChanged("cura/active_mode")
|
||||
|
||||
def event(self, event):
|
||||
return False
|
||||
|
||||
def getModel(self):
|
||||
if not self._model:
|
||||
self._model = PerObjectSettingsModel.PerObjectSettingsModel()
|
||||
|
||||
#For some reason, casting this model to itself causes the model to properly be cast to a QVariant, even though it ultimately inherits from QVariant.
|
||||
#Yeah, we have no idea either...
|
||||
return PerObjectSettingsModel.PerObjectSettingsModel(self._model)
|
||||
|
||||
def getSelectedIndex(self):
|
||||
try:
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
if selected_object.getParent().callDecoration("isGroup"):
|
||||
selected_object = selected_object.getParent()
|
||||
except:
|
||||
selected_object = None
|
||||
def getSelectedObjectId(self):
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
selected_object_id = id(selected_object)
|
||||
index = self.getModel().find("id", selected_object_id)
|
||||
return index
|
||||
return selected_object_id
|
||||
|
||||
def getContainerID(self):
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
try:
|
||||
return selected_object.callDecoration("getStack").getId()
|
||||
except AttributeError:
|
||||
return ""
|
||||
|
||||
## Gets the active extruder of the currently selected object.
|
||||
#
|
||||
# \return The active extruder of the currently selected object.
|
||||
def getSelectedActiveExtruder(self):
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
return selected_object.callDecoration("getActiveExtruder")
|
||||
|
||||
## Changes the active extruder of the currently selected object.
|
||||
#
|
||||
# \param extruder_stack_id The ID of the extruder to print the currently
|
||||
# selected object with.
|
||||
def setSelectedActiveExtruder(self, extruder_stack_id):
|
||||
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:
|
||||
selected_object.addDecorator(SettingOverrideDecorator())
|
||||
selected_object.callDecoration("setActiveExtruder", extruder_stack_id)
|
||||
|
||||
def _onPreferenceChanged(self, preference):
|
||||
if preference == "cura/active_mode":
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
class SettingOverrideModel(ListModel):
|
||||
KeyRole = Qt.UserRole + 1
|
||||
LabelRole = Qt.UserRole + 2
|
||||
DescriptionRole = Qt.UserRole + 3
|
||||
ValueRole = Qt.UserRole + 4
|
||||
TypeRole = Qt.UserRole + 5
|
||||
UnitRole = Qt.UserRole + 6
|
||||
ValidRole = Qt.UserRole + 7
|
||||
OptionsRole = Qt.UserRole + 8
|
||||
WarningDescriptionRole = Qt.UserRole + 9
|
||||
ErrorDescriptionRole = Qt.UserRole + 10
|
||||
GlobalOnlyRole = Qt.UserRole + 11
|
||||
|
||||
def __init__(self, node, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._ignore_setting_change = None
|
||||
|
||||
self._node = node
|
||||
self._node.decoratorsChanged.connect(self._onDecoratorsChanged)
|
||||
self._onDecoratorsChanged(None)
|
||||
|
||||
self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile() #To be able to get notified when a setting changes.
|
||||
self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged)
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onProfileChanged)
|
||||
|
||||
self.addRoleName(self.KeyRole, "key")
|
||||
self.addRoleName(self.LabelRole, "label")
|
||||
self.addRoleName(self.DescriptionRole, "description")
|
||||
self.addRoleName(self.ValueRole,"value")
|
||||
self.addRoleName(self.TypeRole, "type")
|
||||
self.addRoleName(self.UnitRole, "unit")
|
||||
self.addRoleName(self.ValidRole, "valid")
|
||||
self.addRoleName(self.OptionsRole, "options")
|
||||
self.addRoleName(self.WarningDescriptionRole, "warning_description")
|
||||
self.addRoleName(self.ErrorDescriptionRole, "error_description")
|
||||
self.addRoleName(self.GlobalOnlyRole, "global_only")
|
||||
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setSettingValue(self, key, value):
|
||||
if not self._decorator:
|
||||
return
|
||||
|
||||
self._decorator.setSettingValue(key, value)
|
||||
|
||||
def _onDecoratorsChanged(self, node):
|
||||
if not self._node.getDecorator(SettingOverrideDecorator):
|
||||
self.clear()
|
||||
return
|
||||
|
||||
self._decorator = self._node.getDecorator(SettingOverrideDecorator)
|
||||
self._decorator.settingAdded.connect(self._onSettingsChanged)
|
||||
self._decorator.settingRemoved.connect(self._onSettingsChanged)
|
||||
self._decorator.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
self._onSettingsChanged()
|
||||
|
||||
def _createOptionsModel(self, options):
|
||||
if not options:
|
||||
return None
|
||||
|
||||
model = ListModel()
|
||||
model.addRoleName(Qt.UserRole + 1, "value")
|
||||
model.addRoleName(Qt.UserRole + 2, "name")
|
||||
for value, name in options.items():
|
||||
model.appendItem({"value": str(value), "name": str(name)})
|
||||
return model
|
||||
|
||||
## Updates the active profile in this model if the active profile is
|
||||
# changed.
|
||||
#
|
||||
# This links the settingValueChanged of the new profile to this model's
|
||||
# _onSettingValueChanged function, so that it properly listens to those
|
||||
# events again.
|
||||
def _onProfileChanged(self):
|
||||
if self._activeProfile: #Unlink from the old profile.
|
||||
self._activeProfile.settingValueChanged.disconnect(self._onProfileSettingValueChanged)
|
||||
old_profile = self._activeProfile
|
||||
self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged) #Re-link to the new profile.
|
||||
for setting_name in old_profile.getChangedSettings().keys(): #Update all changed settings in the old and new profiles.
|
||||
self._onProfileSettingValueChanged(setting_name)
|
||||
for setting_name in self._activeProfile.getChangedSettings().keys():
|
||||
self._onProfileSettingValueChanged(setting_name)
|
||||
|
||||
## Updates the global_only property of a setting once a setting value
|
||||
# changes.
|
||||
#
|
||||
# This method should only get called on settings that are dependent on the
|
||||
# changed setting.
|
||||
#
|
||||
# \param setting_name The setting that needs to be updated.
|
||||
def _onProfileSettingValueChanged(self, setting_name):
|
||||
index = self.find("key", setting_name)
|
||||
if index != -1:
|
||||
self.setProperty(index, "global_only", Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getSetting(setting_name).getGlobalOnly())
|
||||
|
||||
def _onSettingsChanged(self):
|
||||
self.clear()
|
||||
|
||||
items = []
|
||||
for key, setting in self._decorator.getAllSettings().items():
|
||||
value = self._decorator.getSettingValue(key)
|
||||
items.append({
|
||||
"key": key,
|
||||
"label": setting.getLabel(),
|
||||
"description": setting.getDescription(),
|
||||
"value": str(value),
|
||||
"type": setting.getType(),
|
||||
"unit": setting.getUnit(),
|
||||
"valid": setting.validate(value),
|
||||
"options": self._createOptionsModel(setting.getOptions()),
|
||||
"warning_description": setting.getWarningDescription(),
|
||||
"error_description": setting.getErrorDescription(),
|
||||
"global_only": setting.getGlobalOnly()
|
||||
})
|
||||
|
||||
items.sort(key = lambda i: i["key"])
|
||||
|
||||
for item in items:
|
||||
self.appendItem(item)
|
||||
|
||||
def _onSettingValueChanged(self, setting):
|
||||
index = self.find("key", setting.getKey())
|
||||
value = self._decorator.getSettingValue(setting.getKey())
|
||||
if index != -1:
|
||||
self.setProperty(index, "value", str(value))
|
||||
self.setProperty(index, "valid", setting.validate(value))
|
||||
self.setProperty(index, "global_only", setting.getGlobalOnly())
|
|
@ -2,6 +2,8 @@
|
|||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import PerObjectSettingsTool
|
||||
from . import PerObjectSettingVisibilityHandler
|
||||
from PyQt5.QtQml import qmlRegisterType
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
@ -13,7 +15,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"tool": {
|
||||
"name": i18n_catalog.i18nc("@label", "Per Object Settings"),
|
||||
|
@ -25,4 +27,6 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
qmlRegisterType(PerObjectSettingVisibilityHandler.PerObjectSettingVisibilityHandler, "Cura", 1, 0,
|
||||
"PerObjectSettingVisibilityHandler")
|
||||
return { "tool": PerObjectSettingsTool.PerObjectSettingsTool() }
|
||||
|
|
|
@ -29,17 +29,26 @@ class RemovableDriveOutputDevice(OutputDevice):
|
|||
if self._writing:
|
||||
raise OutputDeviceError.DeviceBusyError()
|
||||
|
||||
file_formats = Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() #Formats supported by this application.
|
||||
# Formats supported by this application (File types that we can actually write)
|
||||
file_formats = Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
|
||||
if filter_by_machine:
|
||||
machine_file_formats = Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getFileFormats()
|
||||
file_formats = list(filter(lambda file_format: file_format["mime_type"] in machine_file_formats, file_formats)) #Take the intersection between file_formats and machine_file_formats.
|
||||
container = Application.getInstance().getGlobalContainerStack().findContainer({"file_formats": "*"})
|
||||
|
||||
# Create a list from supported file formats string
|
||||
machine_file_formats = [file_type.strip() for file_type in container.getMetaDataEntry("file_formats").split(";")]
|
||||
|
||||
# Take the intersection between file_formats and machine_file_formats.
|
||||
file_formats = list(filter(lambda file_format: file_format["mime_type"] in machine_file_formats, file_formats))
|
||||
|
||||
if len(file_formats) == 0:
|
||||
Logger.log("e", "There are no file formats available to write with!")
|
||||
raise OutputDeviceError.WriteRequestFailedError()
|
||||
writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(file_formats[0]["mime_type"]) #Just take the first file format available.
|
||||
|
||||
# Just take the first file format available.
|
||||
writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(file_formats[0]["mime_type"])
|
||||
extension = file_formats[0]["extension"]
|
||||
|
||||
if file_name == None:
|
||||
if file_name is None:
|
||||
for n in BreadthFirstIterator(node):
|
||||
if n.getMeshData():
|
||||
file_name = n.getName()
|
||||
|
@ -50,7 +59,7 @@ class RemovableDriveOutputDevice(OutputDevice):
|
|||
Logger.log("e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName())
|
||||
raise OutputDeviceError.WriteRequestFailedError()
|
||||
|
||||
if extension: #Not empty string.
|
||||
if extension: # Not empty string.
|
||||
extension = "." + extension
|
||||
file_name = os.path.join(self.getId(), os.path.splitext(file_name)[0] + extension)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker B.V.",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides removable drive hotplugging and writing support."),
|
||||
"version": "1.0",
|
||||
"api": 2
|
||||
"api": 3
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,12 +34,10 @@ class SolidView(View):
|
|||
self._disabled_shader.setUniformValue("u_diffuseColor", [0.68, 0.68, 0.68, 1.0])
|
||||
self._disabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
||||
|
||||
if Application.getInstance().getMachineManager().getWorkingProfile():
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
if Application.getInstance().getGlobalContainerStack():
|
||||
if Preferences.getInstance().getValue("view/show_overhang"):
|
||||
angle = profile.getSettingValue("support_angle")
|
||||
if angle != None:
|
||||
angle = Application.getInstance().getGlobalContainerStack().getProperty("support_angle", "value")
|
||||
if angle is not None:
|
||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle)))
|
||||
else:
|
||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides a normal solid mesh view."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"view": {
|
||||
"name": i18n_catalog.i18nc("@item:inmenu", "Solid"),
|
||||
|
|
|
@ -256,7 +256,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
if not self.setBaudRate(baud_rate):
|
||||
continue # Could not set the baud rate, go to the next
|
||||
|
||||
time.sleep(1.5) # Ensure that we are not talking to the bootloader. 1.5 sec seems to be the magic number
|
||||
time.sleep(1.5) # Ensure that we are not talking to the bootloader. 1.5 seconds seems to be the magic number
|
||||
sucesfull_responses = 0
|
||||
timeout_time = time.time() + 5
|
||||
self._serial.write(b"\n")
|
||||
|
|
|
@ -128,8 +128,18 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin,
|
|||
return USBPrinterOutputDeviceManager._instance
|
||||
|
||||
def _getDefaultFirmwareName(self):
|
||||
machine_instance = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
machine_type = machine_instance.getMachineDefinition().getId()
|
||||
# Check if there is a valid global container stack
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
||||
self._firmware_view.close()
|
||||
return ""
|
||||
|
||||
# The bottom of the containerstack is the machine definition
|
||||
machine_id = global_container_stack.getBottom().id
|
||||
|
||||
machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value")
|
||||
|
||||
if platform.system() == "Linux":
|
||||
baudrate = 115200
|
||||
else:
|
||||
|
@ -151,23 +161,22 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin,
|
|||
}
|
||||
machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex",
|
||||
}
|
||||
|
||||
##TODO: Add check for multiple extruders
|
||||
hex_file = None
|
||||
if machine_type in machine_without_extras.keys(): # The machine needs to be defined here!
|
||||
if machine_type in machine_with_heated_bed.keys() and machine_instance.getMachineSettingValue("machine_heated_bed"):
|
||||
Logger.log("d", "Choosing firmware with heated bed enabled for machine %s.", machine_type)
|
||||
hex_file = machine_with_heated_bed[machine_type] # Return firmware with heated bed enabled
|
||||
if machine_id in machine_without_extras.keys(): # The machine needs to be defined here!
|
||||
if machine_id in machine_with_heated_bed.keys() and machine_has_heated_bed:
|
||||
Logger.log("d", "Choosing firmware with heated bed enabled for machine %s.", machine_id)
|
||||
hex_file = machine_with_heated_bed[machine_id] # Return firmware with heated bed enabled
|
||||
else:
|
||||
Logger.log("d", "Choosing basic firmware for machine %s.", machine_type)
|
||||
hex_file = machine_without_extras[machine_type] # Return "basic" firmware
|
||||
Logger.log("d", "Choosing basic firmware for machine %s.", machine_id)
|
||||
hex_file = machine_without_extras[machine_id] # Return "basic" firmware
|
||||
else:
|
||||
Logger.log("e", "There is no firmware for machine %s.", machine_type)
|
||||
Logger.log("e", "There is no firmware for machine %s.", machine_id)
|
||||
|
||||
if hex_file:
|
||||
return hex_file.format(baudrate=baudrate)
|
||||
else:
|
||||
Logger.log("e", "Could not find any firmware for machine %s.", machine_type)
|
||||
Logger.log("e", "Could not find any firmware for machine %s.", machine_id)
|
||||
raise FileNotFoundError()
|
||||
|
||||
## Helper to identify serial ports (and scan for them)
|
||||
|
@ -223,7 +232,7 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin,
|
|||
def getSerialPortList(self, only_list_usb = False):
|
||||
base_list = []
|
||||
if platform.system() == "Windows":
|
||||
import winreg
|
||||
import winreg #@UnresolvedImport
|
||||
try:
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
|
||||
i = 0
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"name": i18n_catalog.i18nc("@label", "USB printing"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"api": 2,
|
||||
"api": 3,
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis","Accepts G-Code and sends them to a printer. Plugin can also update firmware.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides the X-Ray view."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"view": {
|
||||
"name": catalog.i18nc("@item:inlistbox", "X-Ray"),
|
||||
|
|
189
plugins/XmlMaterialProfile/XmlMaterialProfile.py
Normal file
189
plugins/XmlMaterialProfile/XmlMaterialProfile.py
Normal file
|
@ -0,0 +1,189 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import math
|
||||
import copy
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
import UM.Settings
|
||||
|
||||
# The namespace is prepended to the tag name but between {}.
|
||||
# We are only interested in the actual tag name, so discard everything
|
||||
# before the last }
|
||||
def _tag_without_namespace(element):
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
|
||||
class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
||||
def __init__(self, container_id, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
|
||||
def serialize(self):
|
||||
raise NotImplementedError("Writing material profiles has not yet been implemented")
|
||||
|
||||
def deserialize(self, serialized):
|
||||
data = ET.fromstring(serialized)
|
||||
|
||||
self.addMetaDataEntry("type", "material")
|
||||
|
||||
# TODO: Add material verfication
|
||||
self.addMetaDataEntry("status", "Unknown")
|
||||
|
||||
metadata = data.iterfind("./um:metadata/*", self.__namespaces)
|
||||
for entry in metadata:
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
|
||||
if tag_name == "name":
|
||||
brand = entry.find("./um:brand", self.__namespaces)
|
||||
material = entry.find("./um:material", self.__namespaces)
|
||||
color = entry.find("./um:color", self.__namespaces)
|
||||
|
||||
self.setName("{0} {1} ({2})".format(brand.text, material.text, color.text))
|
||||
|
||||
self.addMetaDataEntry("brand", brand.text)
|
||||
self.addMetaDataEntry("material", material.text)
|
||||
self.addMetaDataEntry("color_name", color.text)
|
||||
|
||||
continue
|
||||
|
||||
self.addMetaDataEntry(tag_name, entry.text)
|
||||
|
||||
property_values = {}
|
||||
properties = data.iterfind("./um:properties/*", self.__namespaces)
|
||||
for entry in properties:
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
property_values[tag_name] = entry.text
|
||||
|
||||
diameter = float(property_values.get("diameter", 2.85)) # In mm
|
||||
density = float(property_values.get("density", 1.3)) # In g/cm3
|
||||
|
||||
weight_per_cm = (math.pi * (diameter / 20) ** 2 * 0.1) * density
|
||||
|
||||
spool_weight = property_values.get("spool_weight")
|
||||
spool_length = property_values.get("spool_length")
|
||||
if spool_weight:
|
||||
length = float(spool_weight) / weight_per_cm
|
||||
property_values["spool_length"] = str(length / 100)
|
||||
elif spool_length:
|
||||
weight = (float(spool_length) * 100) * weight_per_cm
|
||||
property_values["spool_weight"] = str(weight)
|
||||
|
||||
self.addMetaDataEntry("properties", property_values)
|
||||
|
||||
self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])
|
||||
|
||||
global_setting_values = {}
|
||||
settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__material_property_setting_map:
|
||||
self.setProperty(self.__material_property_setting_map[key], "value", entry.text, self._definition)
|
||||
global_setting_values[self.__material_property_setting_map[key]] = entry.text
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
|
||||
for machine in machines:
|
||||
machine_setting_values = {}
|
||||
settings = machine.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__material_property_setting_map:
|
||||
machine_setting_values[self.__material_property_setting_map[key]] = entry.text
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces)
|
||||
for identifier in identifiers:
|
||||
machine_id = self.__product_id_map.get(identifier.get("product"), None)
|
||||
if machine_id is None:
|
||||
Logger.log("w", "Cannot create material for unknown machine %s", machine_id)
|
||||
continue
|
||||
|
||||
definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
|
||||
if not definitions:
|
||||
Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
continue
|
||||
|
||||
definition = definitions[0]
|
||||
|
||||
new_material = XmlMaterialProfile(self.id + "_" + machine_id)
|
||||
new_material.setName(self.getName())
|
||||
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_material.setDefinition(definition)
|
||||
|
||||
for key, value in global_setting_values.items():
|
||||
new_material.setProperty(key, "value", value, definition)
|
||||
|
||||
for key, value in machine_setting_values.items():
|
||||
new_material.setProperty(key, "value", value, definition)
|
||||
|
||||
new_material._dirty = False
|
||||
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)
|
||||
|
||||
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
||||
for hotend in hotends:
|
||||
hotend_id = hotend.get("id")
|
||||
if hotend_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
|
||||
if not variant_containers:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)
|
||||
|
||||
if not variant_containers:
|
||||
Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
|
||||
continue
|
||||
|
||||
new_hotend_material = XmlMaterialProfile(self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_"))
|
||||
new_hotend_material.setName(self.getName())
|
||||
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_hotend_material.setDefinition(definition)
|
||||
|
||||
new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
|
||||
|
||||
for key, value in global_setting_values.items():
|
||||
new_hotend_material.setProperty(key, "value", value, definition)
|
||||
|
||||
for key, value in machine_setting_values.items():
|
||||
new_hotend_material.setProperty(key, "value", value, definition)
|
||||
|
||||
settings = hotend.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__material_property_setting_map:
|
||||
new_hotend_material.setProperty(self.__material_property_setting_map[key], "value", entry.text, definition)
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
new_hotend_material._dirty = False
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material)
|
||||
|
||||
|
||||
# Map XML file setting names to internal names
|
||||
__material_property_setting_map = {
|
||||
"print temperature": "material_print_temperature",
|
||||
"heated bed temperature": "material_bed_temperature",
|
||||
"standby temperature": "material_standby_temperature",
|
||||
"print cooling": "cool_fan_speed",
|
||||
"retraction amount": "retraction_amount",
|
||||
"retraction speed": "retraction_speed",
|
||||
}
|
||||
|
||||
# Map XML file product names to internal ids
|
||||
__product_id_map = {
|
||||
"Ultimaker2": "ultimaker2",
|
||||
"Ultimaker2+": "ultimaker2_plus",
|
||||
"Ultimaker2go": "ultimaker2_go",
|
||||
"Ultimaker2extended": "ultimaker2_extended",
|
||||
"Ultimaker2extended+": "ultimaker2_extended_plus",
|
||||
"Ultimaker Original": "ultimaker_original",
|
||||
"Ultimaker Original+": "ultimaker_original_plus"
|
||||
}
|
||||
|
||||
__namespaces = {
|
||||
"um": "http://www.ultimaker.com/material"
|
||||
}
|
32
plugins/XmlMaterialProfile/__init__.py
Normal file
32
plugins/XmlMaterialProfile/__init__.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import XmlMaterialProfile
|
||||
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Material Profiles"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides capabilities to read and write XML-based material profiles."),
|
||||
"api": 3
|
||||
},
|
||||
"settings_container": {
|
||||
"mimetype": "application/x-ultimaker-material-profile"
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
mime_type = MimeType(
|
||||
name = "application/x-ultimaker-material-profile",
|
||||
comment = "Ultimaker Material Profile",
|
||||
suffixes = [ "xml.fdm_material" ]
|
||||
)
|
||||
MimeTypeDatabase.addMimeType(mime_type)
|
||||
return { "settings_container": XmlMaterialProfile.XmlMaterialProfile("default_xml_material_profile") }
|
||||
|
87
resources/definitions/bq_hephestos.def.json
Normal file
87
resources/definitions/bq_hephestos.def.json
Normal file
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"id": "bq_hephestos",
|
||||
"name": "BQ Prusa i3 Hephestos",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "BQ",
|
||||
"manufacturer": "BQ",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "bq_hephestos_platform.stl",
|
||||
"platform_offset": [ 0, -82, 0]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 215
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 180
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 220
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 0
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_wall": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 120
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 20
|
||||
},
|
||||
"support_enable": {
|
||||
"default_value": true
|
||||
}
|
||||
}
|
||||
}
|
47
resources/definitions/bq_hephestos_2.def.json
Normal file
47
resources/definitions/bq_hephestos_2.def.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"id": "bq_hephestos_2",
|
||||
"version": 2,
|
||||
"name": "BQ Hephestos 2",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "BQ",
|
||||
"manufacturer": "BQ",
|
||||
"category": "Other",
|
||||
"platform": "bq_hephestos_2_platform.stl",
|
||||
"platform_offset": [6, 1320, 0 ],
|
||||
"file_formats": "text/x-gcode"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --" },
|
||||
"machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" },
|
||||
"machine_width": { "default_value": 210 },
|
||||
"machine_depth": { "default_value": 297 },
|
||||
"machine_height": { "default_value": 220 },
|
||||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"material_print_temperature": { "default_value": 210 },
|
||||
"material_bed_temperature": { "default_value": 0 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"layer_height_0": { "default_value": 0.2 },
|
||||
"wall_line_count": { "default_value": 3 },
|
||||
"wall_thickness": { "default_value": 1.2 },
|
||||
"top_bottom_thickness": { "default_value": 1.2 },
|
||||
"infill_sparse_density": { "default_value": 20 },
|
||||
"infill_overlap": { "default_value": 15 },
|
||||
"speed_print": { "default_value": 60 },
|
||||
"speed_travel": { "default_value": 160 },
|
||||
"speed_layer_0": { "default_value": 30 },
|
||||
"speed_wall_x": { "default_value": 35 },
|
||||
"speed_wall_0": { "default_value": 30 },
|
||||
"speed_infill": { "default_value": 80 },
|
||||
"speed_topbottom": { "default_value": 35 },
|
||||
"skirt_speed": { "default_value": 35 },
|
||||
"skirt_line_count": { "default_value": 4 },
|
||||
"skirt_minimal_length": { "default_value": 30 },
|
||||
"skirt_gap": { "default_value": 6 },
|
||||
"cool_fan_full_at_height": { "default_value": 0.4 }
|
||||
}
|
||||
}
|
87
resources/definitions/bq_hephestos_xl.def.json
Normal file
87
resources/definitions/bq_hephestos_xl.def.json
Normal file
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"id": "bq_hephestos_xl",
|
||||
"version": 2,
|
||||
"name": "BQ Prusa i3 Hephestos XL",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"manufacturer": "BQ",
|
||||
"author": "BQ",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-code",
|
||||
"platform": "bq_hephestos_platform.stl",
|
||||
"platform_offset": [ 0, -82, 0]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 300
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 180
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 220
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 0
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_wall": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 120
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 20
|
||||
},
|
||||
"support_enable": {
|
||||
"default_value": true
|
||||
}
|
||||
}
|
||||
}
|
88
resources/definitions/bq_witbox.def.json
Normal file
88
resources/definitions/bq_witbox.def.json
Normal file
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"id": "bq_witbox",
|
||||
"version": 2,
|
||||
"name": "BQ Witbox",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "BQ",
|
||||
"manufacturer": "BQ",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "bq_witbox_platform.stl",
|
||||
"platform_offset": [ 0, -145, -38]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG90 ;set to absolute positioning\nG1 Z200 ;move the platform to the bottom\nG28 X0 Y0 ;move to the X/Y origin (Home)\nM84 ;turn off steppers\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 297
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 220
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 0
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_wall": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 120
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 20
|
||||
},
|
||||
"support_enable": {
|
||||
"default_value": true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
111
resources/definitions/bq_witbox_2.def.json
Normal file
111
resources/definitions/bq_witbox_2.def.json
Normal file
|
@ -0,0 +1,111 @@
|
|||
{
|
||||
"id": "bq_witbox_2",
|
||||
"version": 2,
|
||||
"name": "BQ Witbox 2",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "BQ",
|
||||
"manufacturer": "BQ",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "bq_witbox_platform.stl",
|
||||
"platform_offset": [0, -145, -38]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 297
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 210
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 0
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_line_count": {
|
||||
"default_value": 3
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 1.2
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 1.2
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"default_value": 20
|
||||
},
|
||||
"infill_overlap": {
|
||||
"default_value": 15
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 160
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"default_value": 35
|
||||
},
|
||||
"speed_wall_0": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 80
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 35
|
||||
},
|
||||
"skirt_speed": {
|
||||
"default_value": 35
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 4
|
||||
},
|
||||
"skirt_minimal_length": {
|
||||
"default_value": 30
|
||||
},
|
||||
"skirt_gap": {
|
||||
"default_value": 6
|
||||
},
|
||||
"cool_fan_full_at_height": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"support_enable": {
|
||||
"default_value": false
|
||||
}
|
||||
}
|
||||
}
|
151
resources/definitions/fdmextruder.def.json
Normal file
151
resources/definitions/fdmextruder.def.json
Normal file
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"id": "fdmextruder",
|
||||
"name": "Extruder",
|
||||
"version": 2,
|
||||
"metadata":
|
||||
{
|
||||
"type": "extruder",
|
||||
"author": "Ultimaker B.V.",
|
||||
"manufacturer": "Ultimaker",
|
||||
"visible": false
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"machine_settings":
|
||||
{
|
||||
"label": "Machine",
|
||||
"type": "category",
|
||||
"description": "Machine specific settings",
|
||||
"children":
|
||||
{
|
||||
"extruder_nr":
|
||||
{
|
||||
"label": "Extruder",
|
||||
"description": "The extruder train used for printing. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_nozzle_offset_x":
|
||||
{
|
||||
"label": "Nozzle X Offset",
|
||||
"description": "The x-coordinate of the offset of the nozzle.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_nozzle_offset_y":
|
||||
{
|
||||
"label": "Nozzle Y Offset",
|
||||
"description": "The y-coordinate of the offset of the nozzle.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_start_code":
|
||||
{
|
||||
"label": "Extruder Start G-Code",
|
||||
"description": "Start g-code to execute whenever turning the extruder on.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_start_pos_abs":
|
||||
{
|
||||
"label": "Extruder Start Position Absolute",
|
||||
"description": "Make the extruder starting position absolute rather than relative to the last-known location of the head.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_start_pos_x":
|
||||
{
|
||||
"label": "Extruder Start Position X",
|
||||
"description": "The x-coordinate of the starting position when turning the extruder on.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_start_pos_y":
|
||||
{
|
||||
"label": "Extruder Start Position Y",
|
||||
"description": "The y-coordinate of the starting position when turning the extruder on.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_end_code":
|
||||
{
|
||||
"label": "Extruder End G-Code",
|
||||
"description": "End g-code to execute whenever turning the extruder off.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_end_pos_abs":
|
||||
{
|
||||
"label": "Extruder End Position Absolute",
|
||||
"description": "Make the extruder ending position absolute rather than relative to the last-known location of the head.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_end_pos_x":
|
||||
{
|
||||
"label": "Extruder End Position X",
|
||||
"description": "The x-coordinate of the ending position when turning the extruder off.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"machine_extruder_end_pos_y":
|
||||
{
|
||||
"label": "Extruder End Position Y",
|
||||
"description": "The y-coordinate of the ending position when turning the extruder off.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3572
resources/definitions/fdmprinter.def.json
Normal file
3572
resources/definitions/fdmprinter.def.json
Normal file
File diff suppressed because it is too large
Load diff
59
resources/definitions/grr_neo.def.json
Normal file
59
resources/definitions/grr_neo.def.json
Normal file
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"id": "grr_neo",
|
||||
"version": 2,
|
||||
"name": "German RepRap Neo",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Simon Cor",
|
||||
"manufacturer": "German RepRap",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "grr_neo_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 150
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 150
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 150
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.5
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"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_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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
99
resources/definitions/innovo_inventor.def.json
Normal file
99
resources/definitions/innovo_inventor.def.json
Normal file
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"id": "innovo-inventor",
|
||||
"version": 2,
|
||||
"name": "Innovo INVENTOR",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Adam Rumjahn",
|
||||
"manufacturer": "Innovo",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "inventor_platform.stl",
|
||||
"platform_offset": [-180, -0.25, 160]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 340
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 290
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 300
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[-43.7, -19.2],
|
||||
[-43.7, 55],
|
||||
[43.7, 55],
|
||||
[43.7, -19.2]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 82.3
|
||||
},
|
||||
"machine_nozzle_offset_x": {
|
||||
"default_value": 0
|
||||
},
|
||||
"machine_nozzle_offset_y": {
|
||||
"default_value": 15
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\n{IF_BED}M190 S{BED}\n{IF_EXT0}M104 T0 S{TEMP0}\n{IF_EXT0}M109 T0 S{TEMP0}\n{IF_EXT1}M104 T1 S{TEMP1}\n{IF_EXT1}M109 T1 S{TEMP1}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.15
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 0.8
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 0.3
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 215
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 60
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 100
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 150
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 30.0,
|
||||
"minimum_value": 0.1
|
||||
},
|
||||
"infill_overlap": {
|
||||
"default_value": 10.0
|
||||
}
|
||||
}
|
||||
}
|
57
resources/definitions/m180.def.json
Normal file
57
resources/definitions/m180.def.json
Normal file
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"id": "m180",
|
||||
"version": 2,
|
||||
"name": "Malyan M180",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Ruben Dulek",
|
||||
"manufacturer": "Malyan",
|
||||
"category": "Other",
|
||||
"file_formats": "application/x3g"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 230
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 165
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 145
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4,
|
||||
"minimum_value": "0.001"
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[ -75, 35 ],
|
||||
[ -75, -18 ],
|
||||
[ 18, -18 ],
|
||||
[ 18, 35 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": "M136\nM73 P0\nM103\nG21\nG90\nM320\n;(**** begin homing ****)\nG162 X Y F4000\nG161 Z F3500\nG92 Z-5\nG1 Z0.0\nG161 Z F100\nM132 X Y Z A B\n;(**** end homing ****)\nG92 X147 Y66 Z5\nG1 X105 Y-60 Z10 F4000.0\nG130 X127 Y127 A127 B127\nG0 X105 Y-60\nG1 Z0.3 F300\nG92 E0\nG1 X100 E10 F300\nG92 E0\nG1 Z0.0 F300\nM320"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G92 Z0\nG1 Z10 F400\nM18\nM109 S0 T0\nM104 S0 T0\nM73 P100 (end build progress)\nG162 X Y F3000\nM18"
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75,
|
||||
"minimum_value_warning": "1.5",
|
||||
"maximum_value_warning": "2.0"
|
||||
}
|
||||
}
|
||||
}
|
176
resources/definitions/maker_starter.def.json
Normal file
176
resources/definitions/maker_starter.def.json
Normal file
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"id": "maker_starter",
|
||||
"version": 2,
|
||||
"name": "3DMaker Starter",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "tvlgiao",
|
||||
"manufacturer": "3DMaker",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "makerstarter_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 210
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 185
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": []
|
||||
},
|
||||
"machine_nozzle_tip_outer_diameter": {
|
||||
"default_value": 1
|
||||
},
|
||||
"machine_nozzle_head_distance": {
|
||||
"default_value": 3
|
||||
},
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default_value": 45
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_line_count": {
|
||||
"default_value": 2
|
||||
},
|
||||
"top_layers": {
|
||||
"default_value": 4
|
||||
},
|
||||
"bottom_layers": {
|
||||
"default_value": 4
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 50
|
||||
},
|
||||
"speed_wall": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_wall_0": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"default_value": 30
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 50
|
||||
},
|
||||
"speed_support": {
|
||||
"default_value": 50
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 120
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 20
|
||||
},
|
||||
"skirt_speed": {
|
||||
"default_value": 15
|
||||
},
|
||||
"speed_slowdown_layers": {
|
||||
"default_value": 4
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"default_value": 20
|
||||
},
|
||||
"cool_fan_speed_min": {
|
||||
"default_value": 50
|
||||
},
|
||||
"cool_fan_speed_max": {
|
||||
"default_value": 100
|
||||
},
|
||||
"cool_fan_full_layer": {
|
||||
"default_value": 4
|
||||
},
|
||||
"cool_min_layer_time": {
|
||||
"default_value": 5
|
||||
},
|
||||
"cool_min_layer_time_fan_speed_max": {
|
||||
"default_value": 10
|
||||
},
|
||||
"support_type": {
|
||||
"default_value": "Everywhere"
|
||||
},
|
||||
"support_angle": {
|
||||
"default_value": 45
|
||||
},
|
||||
"support_xy_distance": {
|
||||
"default_value": 1
|
||||
},
|
||||
"support_z_distance": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"support_top_distance": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"support_bottom_distance": {
|
||||
"default_value": 0.24
|
||||
},
|
||||
"support_pattern": {
|
||||
"default_value": "ZigZag"
|
||||
},
|
||||
"support_infill_rate": {
|
||||
"default_value": 15
|
||||
},
|
||||
"adhesion_type": {
|
||||
"default_value": "Raft"
|
||||
},
|
||||
"skirt_minimal_length": {
|
||||
"default_value": 100
|
||||
},
|
||||
"raft_base_line_spacing": {
|
||||
"default_value": 2
|
||||
},
|
||||
"raft_base_thickness": {
|
||||
"default_value": 0.3
|
||||
},
|
||||
"raft_base_line_width": {
|
||||
"default_value": 2
|
||||
},
|
||||
"raft_base_speed": {
|
||||
"default_value": 15
|
||||
},
|
||||
"raft_interface_thickness": {
|
||||
"default_value": 0.24
|
||||
},
|
||||
"raft_interface_line_width": {
|
||||
"default_value": 0.6
|
||||
},
|
||||
"raft_airgap": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"raft_surface_layers": {
|
||||
"default_value": 2
|
||||
}
|
||||
}
|
||||
}
|
94
resources/definitions/mendel90.def.json
Normal file
94
resources/definitions/mendel90.def.json
Normal file
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"id": "mendel90",
|
||||
"name": "Mendel90",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Bo Herrmannsen",
|
||||
"category": "Other",
|
||||
"manufacturer": "Nophead",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "mendel90_platform.stl",
|
||||
"platform_offset": [0, -23.6, 0]
|
||||
},
|
||||
|
||||
"pages": [
|
||||
"BedLeveling"
|
||||
],
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\nM107 ;start with the fan off\nG1 X90 Y200 F6000 ;go to the middle of the front\nG1 Z0.05 ;close to the bed\nG1 Z0.3 ;lift Z\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM107 ;carriage fan off\nG91 ;relative positioning\nG1 Z10 ;Move up Z 10mm\nG90 ;back to absolute mode\nG1 E-1 F1200 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG92 E0 ;zero the extruded length\nG1 Y200 F5000 ;Move Y to middle of bed cooling fan\nM42 P42 S255 ;Turn on Bed cooling fan on\nG4 S420 ;Wait 7 mins\nM42 P42 S0 ;Turn off bed cooling fan\nG1 Y10 F5000 ;Move Y to front\nM84 ;steppers off\n"
|
||||
},
|
||||
"material_bed_temp_wait": {
|
||||
"default_value": true
|
||||
},
|
||||
"material_print_temp_prepend": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 1
|
||||
},
|
||||
"machine_nozzle_tip_outer_diameter": {
|
||||
"default_value": 1
|
||||
},
|
||||
"machine_nozzle_head_distance": {
|
||||
"default_value": 5
|
||||
},
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default_value": 45
|
||||
},
|
||||
"machine_heat_zone_length": {
|
||||
"default_value": 16
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2.0
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2.0
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[ -12, 9 ],
|
||||
[ -12, -9 ],
|
||||
[ 14, 9 ],
|
||||
[ 14, -9 ]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
41
resources/definitions/printrbot_simple.def.json
Normal file
41
resources/definitions/printrbot_simple.def.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"id": "printrbot_simple",
|
||||
"version": 2,
|
||||
"name": "Printrbot Simple",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Calvindog717",
|
||||
"manufacturer": "PrintrBot",
|
||||
"file_formats": "text/x-gcode"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_width": { "default_value": 150 },
|
||||
"machine_height": { "default_value": 150 },
|
||||
"machine_depth": { "default_value": 140 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_nozzle_size": { "default_value": 0.3 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
||||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[ 55, -20 ],
|
||||
[ 55, 99999 ],
|
||||
[ -49, 99999 ],
|
||||
[ -49, -20 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": { "default_value": 99999 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"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\nG29 ; auto bed-levelling\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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
65
resources/definitions/prusa_i3.def.json
Normal file
65
resources/definitions/prusa_i3.def.json
Normal file
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"id": "prusa_i3",
|
||||
"version": 2,
|
||||
"name": "Prusa i3",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Quillford",
|
||||
"manufacturer": "Prusajr",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2",
|
||||
"platform": "prusai3_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"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_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"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_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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
65
resources/definitions/prusa_i3_xl.def.json
Normal file
65
resources/definitions/prusa_i3_xl.def.json
Normal file
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"id": "prusa_i3_xl",
|
||||
"version": 2,
|
||||
"name": "Prusa i3 xl",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "guigashm",
|
||||
"manufacturer": "Prusajr",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "prusai3_xl_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 270
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2.0
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2.0
|
||||
},
|
||||
"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_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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
102
resources/definitions/rigidbot.def.json
Normal file
102
resources/definitions/rigidbot.def.json
Normal file
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"id": "rigidbot",
|
||||
"version": 2,
|
||||
"name": "RigidBot",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "RBC",
|
||||
"manufacturer": "RigidBot",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "rigidbot_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 254
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 254
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 254
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 0
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": ";End GCode\nM104 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+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 0.8
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 0.3
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 195
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 60
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 100
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 15
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 150
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 15,
|
||||
"minimum_value": "0.1"
|
||||
},
|
||||
"infill_overlap": {
|
||||
"default_value": 10
|
||||
},
|
||||
"cool_fan_enabled": {
|
||||
"default_value": false
|
||||
},
|
||||
"cool_fan_speed": {
|
||||
"default_value": 0
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 3,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
},
|
||||
"skirt_gap": {
|
||||
"default_value": 4,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
},
|
||||
"skirt_minimal_length": {
|
||||
"default_value": 200,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
}
|
||||
}
|
||||
}
|
105
resources/definitions/rigidbot_big.def.json
Normal file
105
resources/definitions/rigidbot_big.def.json
Normal file
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"id": "rigidbotbig",
|
||||
"version": 2,
|
||||
"name": "RigidBotBig",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "RBC",
|
||||
"manufacturer": "RigidBot",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "rigidbotbig_platform.stl"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 400
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 300
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 254
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 0
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": ";End GCode\nM104 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+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 0.8
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"default_value": 0.3
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_value": 195
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_value": 60
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"speed_infill": {
|
||||
"default_value": 100
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"default_value": 15
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 150
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 15,
|
||||
"minimum_value": "0.1"
|
||||
},
|
||||
"infill_overlap": {
|
||||
"default_value": 10
|
||||
},
|
||||
"cool_fan_enabled": {
|
||||
"default_value": false
|
||||
},
|
||||
"cool_fan_speed": {
|
||||
"default_value": 0
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 3,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
},
|
||||
"skirt_gap": {
|
||||
"default_value": 4,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
},
|
||||
"skirt_minimal_length": {
|
||||
"default_value": 200,
|
||||
"enabled": "adhesion_type == \"Skirt\""
|
||||
}
|
||||
}
|
||||
}
|
11
resources/definitions/ultimaker.def.json
Normal file
11
resources/definitions/ultimaker.def.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "ultimaker_base",
|
||||
"version": 2,
|
||||
"name": "Ultimaker",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"visible": false
|
||||
}
|
||||
}
|
108
resources/definitions/ultimaker2.def.json
Normal file
108
resources/definitions/ultimaker2.def.json
Normal file
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"id": "ultimaker2",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2",
|
||||
"inherits": "ultimaker",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"platform_offset": [9, 0, 0]
|
||||
},
|
||||
"overrides": {
|
||||
"machine_start_gcode" : {
|
||||
"default_value": ""
|
||||
},
|
||||
"machine_end_gcode" : {
|
||||
"default_value": ""
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 223
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 223
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 205
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[ -42, 12 ],
|
||||
[ -42, -32 ],
|
||||
[ 62, 12 ],
|
||||
[ 62, -32 ]
|
||||
]
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4,
|
||||
"minimum_value": "0.001"
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_use_extruder_offset_to_offset_coords": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "UltiGCode"
|
||||
},
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": [
|
||||
[[-115, 112.5], [ -82, 112.5], [ -84, 102.5], [-115, 102.5]],
|
||||
[[ 115, 112.5], [ 115, 102.5], [ 110, 102.5], [ 108, 112.5]],
|
||||
[[-115, -112.5], [-115, -104.5], [ -84, -104.5], [ -82, -112.5]],
|
||||
[[ 115, -112.5], [ 108, -112.5], [ 110, -104.5], [ 115, -104.5]]
|
||||
]},
|
||||
"machine_nozzle_tip_outer_diameter": {
|
||||
"default_value": 1
|
||||
},
|
||||
"machine_nozzle_head_distance": {
|
||||
"default_value": 3
|
||||
},
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default_value": 45
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"material_diameter": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"material_flow": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"retraction_amount": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"retraction_speed": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"retraction_retract_speed": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"enabled": "False"
|
||||
}
|
||||
}
|
||||
}
|
21
resources/definitions/ultimaker2_extended.def.json
Normal file
21
resources/definitions/ultimaker2_extended.def.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "ultimaker2_extended",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2 Extended",
|
||||
"inherits": "ultimaker2",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2Extendedbackplate.png"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_height": {
|
||||
"default_value": 315
|
||||
}
|
||||
}
|
||||
}
|
29
resources/definitions/ultimaker2_extended_plus.def.json
Normal file
29
resources/definitions/ultimaker2_extended_plus.def.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"id": "ultimaker2_extended_plus",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2 Extended+",
|
||||
"inherits": "ultimaker2_plus",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_height": {
|
||||
"default_value": 313
|
||||
},
|
||||
"machine_show_variants": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_head_distance": {
|
||||
"default_value": 5
|
||||
},
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default_value": 45
|
||||
}
|
||||
}
|
||||
}
|
39
resources/definitions/ultimaker2_go.def.json
Normal file
39
resources/definitions/ultimaker2_go.def.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"id": "ultimaker2_go",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2 Go",
|
||||
"inherits": "ultimaker2",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2go_platform.obj",
|
||||
"platform_texture": "Ultimaker2Gobackplate.png",
|
||||
"platform_offset": [0, 0, 0]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 120
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 120
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 115
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": [
|
||||
[[-60, 60], [-33, 60], [-35, 52], [-60, 52]],
|
||||
[[ 60, 60], [ 60, 52], [ 35, 52], [ 33, 60]],
|
||||
[[-60, -60], [-60, -52], [-35, -52], [-33, -60]],
|
||||
[[ 60, -60], [ 33, -60], [ 35, -52], [ 60, -52]]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
77
resources/definitions/ultimaker2_plus.def.json
Normal file
77
resources/definitions/ultimaker2_plus.def.json
Normal file
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"id": "ultimaker2_plus",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2+",
|
||||
"inherits": "ultimaker2",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2Plusbackplate.png",
|
||||
"preferred_variant": "ultimaker2_plus_0.4",
|
||||
"preferred_material": "*pla*",
|
||||
"preferred_quality": "*normal*",
|
||||
"has_variants": true,
|
||||
"has_materials": true,
|
||||
"has_machine_materials": true,
|
||||
"has_machine_quality": true
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"layer_height_0": {
|
||||
"value": "round(machine_nozzle_size / 1.5, 2)"
|
||||
},
|
||||
"line_width": {
|
||||
"value": "round(machine_nozzle_size * 0.875, 2)"
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"default_value": 20
|
||||
},
|
||||
"speed_support": {
|
||||
"value": "speed_wall_0"
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 203
|
||||
},
|
||||
"machine_show_variants": {
|
||||
"default_value": true
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 52
|
||||
},
|
||||
"machine_nozzle_head_distance": {
|
||||
"default_value": 5
|
||||
},
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default_value": 45
|
||||
},
|
||||
"machine_heat_zone_length": {
|
||||
"default_value": 20
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[ -44, 14 ],
|
||||
[ -44, -34 ],
|
||||
[ 64, 14 ],
|
||||
[ 64, -34 ]
|
||||
]
|
||||
},
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": [
|
||||
[[-115, 112.5], [ -78, 112.5], [ -80, 102.5], [-115, 102.5]],
|
||||
[[ 115, 112.5], [ 115, 102.5], [ 105, 102.5], [ 103, 112.5]],
|
||||
[[-115, -112.5], [-115, -104.5], [ -84, -104.5], [ -82, -112.5]],
|
||||
[[ 115, -112.5], [ 108, -112.5], [ 110, -104.5], [ 115, -104.5]]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
72
resources/definitions/ultimaker_original.def.json
Normal file
72
resources/definitions/ultimaker_original.def.json
Normal file
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"id": "ultimaker_original",
|
||||
"version": 2,
|
||||
"name": "Ultimaker Original",
|
||||
"inherits": "ultimaker",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker_platform.stl",
|
||||
"has_materials": true,
|
||||
"preferred_material": "*pla*",
|
||||
"preferred_quality": "*normal*",
|
||||
"pages": [
|
||||
"SelectUpgradedParts",
|
||||
"UpgradeFirmware",
|
||||
"UltimakerCheckup",
|
||||
"BedLeveling"
|
||||
]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 205
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 205
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[ -75, 35 ],
|
||||
[ -75, -18 ],
|
||||
[ 18, 35 ],
|
||||
[ 18, -18 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_use_extruder_offset_to_offset_coords": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"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 E6 ;extrude 6 mm 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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
26
resources/definitions/ultimaker_original_plus.def.json
Normal file
26
resources/definitions/ultimaker_original_plus.def.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"id": "ultimaker_original_plus",
|
||||
"version": 2,
|
||||
"name": "Ultimaker Original+",
|
||||
"inherits": "ultimaker_original",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "UltimakerPlusbackplate.png",
|
||||
"pages": [
|
||||
"UpgradeFirmware",
|
||||
"UltimakerCheckup",
|
||||
"BedLeveling"
|
||||
]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
}
|
||||
}
|
||||
}
|
55
resources/definitions/uniqbot_one.def.json
Normal file
55
resources/definitions/uniqbot_one.def.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"id": "uniqbot_one",
|
||||
"version": 2,
|
||||
"name": "Uniqbot",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "Unimatech",
|
||||
"manufacturer": "Unimatech",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_heated_bed": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_width": {
|
||||
"default_value": 140
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 120
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 160
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.5
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"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+5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Der Abstand zwischen der Düse und den horizontalen Abwärtslinien. Bei einem größeren Abstand haben die diagonalen Abwärtslinien einen weniger spitzen Winkel, was wiederum weniger Aufwärtsverbindungen zur nächsten Schicht zur Folge hat. Dies gilt nur für das Drucken mit Drahtstruktur."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Z Überlappung der ersten Schicht"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All model pieces above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "Die erste und die zweite Schicht des Objekts überlappen sich in der Z-Richtung, um das verlorene Filament in dem Luftspalt zu kompensieren. Alle Schichten über der ersten Schicht, verschieben sich in der Z-Richtung mit gewähltem Abstand nach unten."
|
||||
|
|
|
@ -2483,3 +2483,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Distance between the nozzle and horizontally downward lines. Larger clearance results in diagonally downward lines with a less steep angle, which in turn results in fewer upward connections with the next layer. Only applies to Wire Printing."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Initial Layer Z Overlap"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "Make the first and second layer of the object overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount."
|
||||
|
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Distancia entre la tobera y líneas descendentes en horizontal. Cuanto mayor sea la holgura, menos pronunciado será el ángulo de las líneas descendentes en diagonal, lo que a su vez se traduce en menos conexiones ascendentes con la siguiente capa. Solo se aplica a la impresión de alambre."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Superposición de las capas iniciales en Z"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "La superposición entre la primera y segunda capa del objeto para compensar la pérdida de material en el hueco de aire. Todas las capas por encima de la primera capa se desplazan hacia abajo por esta cantidad."
|
||||
|
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr ""
|
||||
|
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Suuttimen ja vaakasuoraan laskevien linjojen välinen etäisyys. Suurempi väli aiheuttaa vähemmän jyrkän kulman diagonaalisesti laskeviin linjoihin, mikä puolestaan johtaa harvempiin yläliitoksiin seuraavan kerroksen kanssa. Koskee vain rautalankamallin tulostusta."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Z Päällekkäisyys Alkukerroksen"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "Tee ensimmäinen ja toinen kerros esineen päällekkäisyys Z-suunnassa kompensoimiseksi filamentti hävisi ilmaväli. Kaikki mallit yläpuolella ensimmäinen malli kerros on siirtynyt alaspäin tämän määrän."
|
||||
|
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Distance entre la buse et les lignes descendantes horizontalement. Un espacement plus important génère des lignes diagonalement descendantes avec un angle moins abrupt, qui génère alors des connexions moins ascendantes avec la couche suivante. Uniquement applicable à l'impression filaire."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "La première et la deuxième couche de l'objet se chevauchent dans la direction Z pour compenser le filament perdu dans l'entrefer. Toutes les chouches au-dessus de la première couce du modèle seront décalées de ce montant."
|
||||
|
|
|
@ -2414,3 +2414,18 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "Indica la distanza tra l'ugello e le linee diagonali verso il basso. Un maggior gioco risulta in linee diagonali verso il basso con un minor angolo di inclinazione, cosa che a sua volta si traduce in meno collegamenti verso l'alto con lo strato successivo. Applicabile solo alla funzione Wire Printing."
|
||||
|
||||
#: fdmprinter.json
|
||||
#, fuzzy
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Z Sovrapposizione Primo Strato"
|
||||
|
||||
#: fdmprinter.json
|
||||
#, fuzzy
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "Effettuare il primo e secondo strato di sovrapposizione oggetto nella direzione Z per compensare il filamento perso nel traferro. Tutti i modelli sopra il primo strato del modello saranno spostate verso il basso di questa quantità."
|
||||
|
|
|
@ -2414,3 +2414,16 @@ msgid ""
|
|||
"which in turn results in less upward connections with the next layer. Only "
|
||||
"applies to Wire Printing."
|
||||
msgstr "De afstand tussen de nozzle en horizontaal neergaande lijnen. Een grotere tussenruimte zorgt voor diagonaal neerwaarts gaande lijnen met een minder steile hoek. Hierdoor ontstaan minder opwaartse verbindingen met de volgende laag. Alleen van toepassing op Draadprinten."
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap label"
|
||||
msgid "Initial Layer Z Overlap"
|
||||
msgstr "Z Overlap Eerste Laag"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "layer_0_z_overlap description"
|
||||
msgid ""
|
||||
"Make the first and second layer of the object overlap in the Z direction to "
|
||||
"compensate for the filament lost in the airgap. All models above the first "
|
||||
"model layer will be shifted down by this amount."
|
||||
msgstr "Laat de eerste en tweede laag overlappen in de Z-richting om te compenseren voor verloren materiaal in de luchtlaag. Alle stukjes model boven de eerste laag worden met deze hoveelheid naar beneden verschoven."
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
{
|
||||
"id": "rigidbot",
|
||||
"version": 1,
|
||||
"name": "RigidBot",
|
||||
"manufacturer": "Other",
|
||||
"author": "RBC",
|
||||
"platform": "rigidbot_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
|
||||
"machine_width": { "default": 254 },
|
||||
"machine_depth": { "default": 254 },
|
||||
"machine_height": { "default": 254 },
|
||||
"machine_heated_bed": { "default": true },
|
||||
|
||||
"machine_nozzle_size": { "default": 0.4,
|
||||
"visible": true
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": { "default": 2.0 },
|
||||
"machine_nozzle_cool_down_speed": { "default": 2.0 },
|
||||
"machine_head_shape_min_x": { "default": 0 },
|
||||
"machine_head_shape_min_y": { "default": 0 },
|
||||
"machine_head_shape_max_x": { "default": 0 },
|
||||
"machine_head_shape_max_y": { "default": 0 },
|
||||
"machine_nozzle_gantry_distance": { "default": 0 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": ";End GCode\nM104 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+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"layer_height": { "default": 0.2 },
|
||||
"wall_thickness": { "default": 0.8 },
|
||||
"top_bottom_thickness": { "default": 0.3, "visible": true },
|
||||
"material_print_temperature": { "default": 195, "visible": true },
|
||||
"material_bed_temperature": { "default": 60, "visible": true },
|
||||
"material_diameter": { "default": 1.75, "visible": true },
|
||||
"speed_print": { "default": 60.0, "visible": true },
|
||||
"speed_infill": { "default": 100.0, "visible": true },
|
||||
"speed_topbottom": { "default": 15.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": "0.1", "default": 15.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 },
|
||||
"cool_fan_enabled": { "default": false, "visible": true },
|
||||
"cool_fan_speed": { "default": 0.0, "visible": true },
|
||||
"skirt_line_count": { "default": 3, "active_if": { "setting": "adhesion_type", "value": "None" } },
|
||||
"skirt_gap": { "default": 4.0, "active_if": { "setting": "adhesion_type", "value": "None" } },
|
||||
"skirt_minimal_length": { "default": 200.0, "active_if": { "setting": "adhesion_type", "value": "None" } }
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
{
|
||||
"id": "rigidbotbig",
|
||||
"version": 1,
|
||||
"name": "RigidBotBig",
|
||||
"manufacturer": "Other",
|
||||
"author": "RBC",
|
||||
"platform": "rigidbotbig_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
|
||||
"machine_width": { "default": 400 },
|
||||
"machine_depth": { "default": 300 },
|
||||
"machine_height": { "default": 254 },
|
||||
"machine_heated_bed": { "default": true },
|
||||
|
||||
"machine_nozzle_size": { "default": 0.4},
|
||||
"machine_nozzle_heat_up_speed": { "default": 2.0 },
|
||||
"machine_nozzle_cool_down_speed": { "default": 2.0 },
|
||||
"machine_head_shape_min_x": { "default": 0 },
|
||||
"machine_head_shape_min_y": { "default": 0 },
|
||||
"machine_head_shape_max_x": { "default": 0 },
|
||||
"machine_head_shape_max_y": { "default": 0 },
|
||||
"machine_nozzle_gantry_distance": { "default": 0 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": ";End GCode\nM104 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+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"layer_height": { "default": 0.2 },
|
||||
"wall_thickness": { "default": 0.8 },
|
||||
"top_bottom_thickness": { "default": 0.3, "visible": true },
|
||||
"material_print_temperature": { "default": 195, "visible": true },
|
||||
"material_bed_temperature": { "default": 60, "visible": true },
|
||||
"material_diameter": { "default": 1.75, "visible": true },
|
||||
"speed_print": { "default": 60.0, "visible": true},
|
||||
"speed_infill": { "default": 100.0, "visible": true },
|
||||
"speed_topbottom": { "default": 15.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": "0.1", "default": 15.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 },
|
||||
"cool_fan_enabled": { "default": false, "visible": true},
|
||||
"cool_fan_speed": { "default": 0.0, "visible": true },
|
||||
"skirt_line_count": { "default": 3, "active_if": { "setting": "adhesion_type", "value": "None" } },
|
||||
"skirt_gap": { "default": 4.0, "active_if": { "setting": "adhesion_type", "value": "None" } },
|
||||
"skirt_minimal_length": { "default": 200.0, "active_if": { "setting": "adhesion_type", "value": "None" } }
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
{
|
||||
"id": "bq_hephestos",
|
||||
"version": 1,
|
||||
"name": "BQ Prusa i3 Hephestos",
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_hephestos_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default": 215
|
||||
},
|
||||
"machine_depth": {
|
||||
"default": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default": 180
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default": "RepRap"
|
||||
},
|
||||
"machine_platform_offset": {
|
||||
"default": [0, -82, 0]
|
||||
},
|
||||
"layer_height": { "default": 0.2 },
|
||||
"layer_height_0": { "default": 0.2, "visible": false },
|
||||
"wall_thickness": { "default": 1.0, "visible": false },
|
||||
"top_bottom_thickness": { "default": 1.0, "visible": false},
|
||||
"bottom_thickness": { "default": 1.0, "visible": false },
|
||||
"material_print_temperature": { "default": 220, "visible": true },
|
||||
"material_bed_temperature": { "default": 0, "visible": false },
|
||||
"material_diameter": { "default": 1.75, "visible": true },
|
||||
"speed_print": { "default": 40.0},
|
||||
"speed_infill": { "default": 40.0, "visible": true },
|
||||
"speed_wall": { "default": 35.0, "visible": true},
|
||||
"speed_topbottom": { "default": 35.0, "visible": true },
|
||||
"speed_travel": { "default": 120.0 },
|
||||
"speed_layer_0": { "default": 20.0, "visible": false },
|
||||
"support_enable": { "default": true }
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
{
|
||||
"id": "bq_hephestos_2",
|
||||
"version": 1,
|
||||
"name": "BQ Hephestos 2",
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_hephestos_2_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": {
|
||||
"default": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --"
|
||||
},
|
||||
"machine_width": {
|
||||
"default": 210
|
||||
},
|
||||
"machine_depth": {
|
||||
"default": 297
|
||||
},
|
||||
"machine_height": {
|
||||
"default": 220
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default": "RepRap"
|
||||
},
|
||||
"machine_platform_offset": {
|
||||
"default": [6, 1320, 0]
|
||||
},
|
||||
"material_print_temperature": { "default": 210.0, "visible": true },
|
||||
"material_bed_temperature": { "default": 0 },
|
||||
"material_diameter": { "default": 1.75 },
|
||||
"layer_height": { "default": 0.2 },
|
||||
"layer_height_0": { "default": 0.2, "visible": true },
|
||||
"wall_line_count": { "default": 3, "visible": false },
|
||||
"wall_thickness": { "default": 1.2, "visible": false },
|
||||
"top_bottom_thickness": { "default": 1.2, "visible": false },
|
||||
"infill_sparse_density": { "default": 20.0 },
|
||||
"infill_overlap": { "default": 15.0, "visible": false },
|
||||
"speed_print": { "default": 60.0 },
|
||||
"speed_travel": { "default": 160.0 },
|
||||
"speed_layer_0": { "default": 30.0, "visible": true },
|
||||
"speed_wall_x": { "default": 35.0, "visible": false },
|
||||
"speed_wall_0": { "default": 30.0, "visible": false },
|
||||
"speed_infill": { "default": 80.0, "visible": true },
|
||||
"speed_topbottom": { "default": 35.0, "visible": false },
|
||||
"skirt_speed": { "default": 35.0, "visible": false },
|
||||
"skirt_line_count": { "default": 4 },
|
||||
"skirt_minimal_length": { "default": 30.0, "visible": false },
|
||||
"skirt_gap": { "default": 6.0 },
|
||||
"cool_fan_full_at_height": { "default": 0.4, "visible": false },
|
||||
"support_enable": { "default": false }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue