mirror of
https://github.com/Ultimaker/Cura.git
synced 2026-03-04 09:34:35 -07:00
Merge pull request #2 from Ultimaker/master
Merging master from upstream
This commit is contained in:
commit
15f84c758b
188 changed files with 9577 additions and 5383 deletions
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -1,13 +1,17 @@
|
|||
*.pyc
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
__pycache__
|
||||
docs/html
|
||||
*.lprof
|
||||
*.log
|
||||
*~
|
||||
# Compiled and generated things.
|
||||
build
|
||||
*.qm
|
||||
*.pyc
|
||||
__pycache__
|
||||
*.mo
|
||||
docs/html
|
||||
*.log
|
||||
resources/i18n/en
|
||||
resources/i18n/x-test
|
||||
|
||||
# Editors and IDEs.
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
*.lprof
|
||||
*~
|
||||
*.qm
|
||||
.idea
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -45,7 +48,7 @@ Third party plugins
|
|||
|
||||
Making profiles for other printers
|
||||
----------------------------------
|
||||
There are two ways of doing it. You can either use the generator [here](http://quillford.github.io/CuraProfileMaker/) or you can use [this](https://github.com/Ultimaker/Cura/blob/master/resources/settings/ultimaker_original.json) as a template.
|
||||
There are two ways of doing it. You can either use the generator [here](http://quillford.github.io/CuraProfileMaker/) or you can use [this](https://github.com/Ultimaker/Cura/blob/master/resources/machines/ultimaker_original.json) as a template.
|
||||
|
||||
* Change the machine ID to something unique
|
||||
* Change the machine_name to your printer's name
|
||||
|
|
@ -57,4 +60,4 @@ There are two ways of doing it. You can either use the generator [here](http://q
|
|||
* Set the start and end gcode in machine_start_gcode and machine_end_gcode
|
||||
* If your printer has a heated bed, set visible to true under material_bed_temperature
|
||||
|
||||
Once you are done, put the profile you have made into resources/settings.
|
||||
Once you are done, put the profile you have made into resources/machines, or in machines in your cura profile folder.
|
||||
|
|
|
|||
|
|
@ -37,13 +37,9 @@ class BuildVolume(SceneNode):
|
|||
|
||||
self.setCalculateBoundingBox(False)
|
||||
|
||||
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
|
||||
|
|
@ -76,7 +72,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
|
||||
|
|
@ -148,9 +144,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!
|
||||
|
|
@ -162,52 +158,49 @@ class BuildVolume(SceneNode):
|
|||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
|
||||
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.
|
||||
|
|
@ -228,8 +221,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],
|
||||
|
|
@ -262,24 +255,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
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
self._convex_hull = None
|
||||
|
||||
|
||||
# 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.
|
||||
self._convex_hull_boundary = None
|
||||
|
||||
|
||||
# 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
|
||||
self._convex_hull_head = None
|
||||
|
|
@ -20,14 +20,22 @@ 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 area with intersection of full head
|
||||
self._convex_hull_head_full = None
|
||||
|
||||
|
||||
self._convex_hull_node = None
|
||||
self._convex_hull_job = None
|
||||
|
||||
# Keep track of the previous parent so we can clear its convex hull when the object is reparented
|
||||
self._parent_node = None
|
||||
|
||||
self._profile = None
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
||||
self._onActiveProfileChanged()
|
||||
#Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
#Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
||||
#self._onActiveProfileChanged()
|
||||
|
||||
def setNode(self, node):
|
||||
super().setNode(node)
|
||||
self._parent_node = node.getParent()
|
||||
node.parentChanged.connect(self._onParentChanged)
|
||||
|
||||
## Force that a new (empty) object is created upon copy.
|
||||
def __deepcopy__(self, memo):
|
||||
|
|
@ -59,7 +67,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
if not self._convex_hull_boundary:
|
||||
return self.getConvexHull()
|
||||
return self._convex_hull_boundary
|
||||
|
||||
|
||||
def setConvexHullBoundary(self, hull):
|
||||
self._convex_hull_boundary = hull
|
||||
|
||||
|
|
@ -68,22 +76,25 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
def setConvexHullHead(self, hull):
|
||||
self._convex_hull_head = hull
|
||||
|
||||
|
||||
def setConvexHull(self, hull):
|
||||
self._convex_hull = hull
|
||||
|
||||
if not hull and self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def getConvexHullJob(self):
|
||||
return self._convex_hull_job
|
||||
|
||||
|
||||
def setConvexHullJob(self, job):
|
||||
self._convex_hull_job = job
|
||||
|
||||
|
||||
def getConvexHullNode(self):
|
||||
return self._convex_hull_node
|
||||
|
||||
|
||||
def setConvexHullNode(self, node):
|
||||
self._convex_hull_node = node
|
||||
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._onSettingValueChanged)
|
||||
|
|
@ -97,15 +108,15 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
if self._convex_hull_job:
|
||||
self._convex_hull_job.cancel()
|
||||
self.setConvexHull(None)
|
||||
if self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def _onSettingValueChanged(self, setting):
|
||||
if setting == "print_sequence":
|
||||
if self._convex_hull_job:
|
||||
self._convex_hull_job.cancel()
|
||||
self.setConvexHull(None)
|
||||
if self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def _onParentChanged(self, node):
|
||||
# Force updating the convex hull of the parent group if the object is in a group
|
||||
if self._parent_node and self._parent_node.callDecoration("isGroup"):
|
||||
self._parent_node.callDecoration("setConvexHull", None)
|
||||
self._parent_node = self.getNode().getParent()
|
||||
|
|
|
|||
|
|
@ -47,10 +47,19 @@ class ConvexHullJob(Job):
|
|||
# This is done to greatly speed up further convex hull calculations as the convex hull
|
||||
# becomes much less complex when dealing with highly detailed models.
|
||||
vertex_data = numpy.round(vertex_data, 1)
|
||||
duplicates = (vertex_data[:,0] == vertex_data[:,1]) | (vertex_data[:,1] == vertex_data[:,2]) | (vertex_data[:,0] == vertex_data[:,2])
|
||||
vertex_data = numpy.delete(vertex_data, numpy.where(duplicates), axis = 0)
|
||||
|
||||
hull = Polygon(vertex_data[:, [0, 2]])
|
||||
vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D.
|
||||
|
||||
# Grab the set of unique points.
|
||||
#
|
||||
# This basically finds the unique rows in the array by treating them as opaque groups of bytes
|
||||
# which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
|
||||
# See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
|
||||
vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
|
||||
_, idx = numpy.unique(vertex_byte_view, return_index=True)
|
||||
vertex_data = vertex_data[idx] # Select the unique rows by index.
|
||||
|
||||
hull = Polygon(vertex_data)
|
||||
|
||||
# First, calculate the normal convex hull around the points
|
||||
hull = hull.getConvexHull()
|
||||
|
|
@ -59,12 +68,12 @@ class ConvexHullJob(Job):
|
|||
# This is done because of rounding errors.
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack:
|
||||
if global_stack.getProperty("print_sequence", "value")== "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
|
||||
self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
|
||||
head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32))
|
||||
head_and_fans = Polygon(numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
||||
|
||||
# Full head hull is used to actually check the order.
|
||||
full_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
|
|
@ -77,7 +86,7 @@ class ConvexHullJob(Job):
|
|||
# Min head hull is used for the push free
|
||||
min_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
self._node.callDecoration("setConvexHullHead", min_head_hull)
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32)))
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(global_stack.getProperty("machine_head_polygon","value"),numpy.float32)))
|
||||
else:
|
||||
self._node.callDecoration("setConvexHullHead", None)
|
||||
if self._node.getParent() is None: # Node was already deleted before job is done.
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ class ConvexHullNode(SceneNode):
|
|||
|
||||
self._original_parent = parent
|
||||
|
||||
self._inherit_orientation = False
|
||||
self._inherit_scale = False
|
||||
|
||||
# Color of the drawn convex hull
|
||||
self._color = Color(35, 35, 35, 128)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from UM.Mesh.ReadMeshJob import ReadMeshJob
|
|||
from UM.Logger import Logger
|
||||
from UM.Preferences import Preferences
|
||||
from UM.JobQueue import JobQueue
|
||||
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
|
||||
|
|
@ -23,6 +23,10 @@ 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
|
||||
|
||||
|
|
@ -34,18 +38,21 @@ from . import CuraActions
|
|||
from . import MultiMaterialDecorator
|
||||
from . import ZOffsetDecorator
|
||||
from . import CuraSplashScreen
|
||||
from . import MachineManagerModel
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
# 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.
|
||||
|
|
@ -63,15 +70,25 @@ 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):
|
||||
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura"))
|
||||
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
|
||||
if not hasattr(sys, "frozen"):
|
||||
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
|
||||
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
|
||||
|
||||
self._open_file_queue = [] # Files to open when plug-ins are loaded.
|
||||
|
||||
# Need to do this before ContainerRegistry tries to load the machines
|
||||
SettingDefinition.addSupportedProperty("global_only", DefinitionPropertyType.Function, default = False)
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion)
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
|
@ -94,30 +111,87 @@ class CuraApplication(QtApplication):
|
|||
self._i18n_catalog = None
|
||||
self._previous_active_tool = None
|
||||
self._platform_activity = False
|
||||
self._scene_boundingbox = AxisAlignedBox()
|
||||
self._scene_bounding_box = AxisAlignedBox()
|
||||
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.ExtruderStack, "extruders")
|
||||
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
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.ExtruderStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
|
||||
|
||||
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
|
||||
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:
|
||||
|
|
@ -126,6 +200,59 @@ 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"
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
@pyqtSlot(result = QUrl)
|
||||
def getDefaultPath(self):
|
||||
return QUrl.fromLocalFile(os.path.expanduser("~/"))
|
||||
|
|
@ -133,6 +260,7 @@ 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.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"))
|
||||
|
|
@ -195,7 +323,11 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
||||
|
||||
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:
|
||||
|
|
@ -206,6 +338,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
|
||||
|
|
@ -265,26 +399,26 @@ 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 = copy.deepcopy(node.getBoundingBox())
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = copy.deepcopy(node.getBoundingBox())
|
||||
else:
|
||||
scene_boundingbox += node.getBoundingBox()
|
||||
scene_bounding_box += node.getBoundingBox()
|
||||
|
||||
if not scene_boundingbox:
|
||||
scene_boundingbox = AxisAlignedBox()
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = AxisAlignedBox()
|
||||
|
||||
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
|
||||
|
|
@ -292,7 +426,9 @@ class CuraApplication(QtApplication):
|
|||
|
||||
@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.
|
||||
# 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()
|
||||
|
|
@ -327,7 +463,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:
|
||||
|
|
@ -346,7 +482,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:
|
||||
|
|
@ -368,7 +504,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:
|
||||
|
|
@ -381,7 +517,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():
|
||||
|
|
@ -392,9 +528,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()
|
||||
|
|
@ -412,9 +548,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)
|
||||
|
||||
|
|
@ -434,9 +570,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:
|
||||
|
|
@ -475,7 +611,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 = ""
|
||||
|
||||
|
|
@ -505,21 +641,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()
|
||||
|
|
@ -538,9 +659,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)
|
||||
|
|
@ -550,40 +672,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()
|
||||
|
|
@ -600,7 +716,7 @@ class CuraApplication(QtApplication):
|
|||
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
|
||||
op.push()
|
||||
|
||||
self.getController().getScene().sceneChanged.emit(node) #Force scene change.
|
||||
self.getController().getScene().sceneChanged.emit(node) #F orce scene change.
|
||||
|
||||
def _onJobFinished(self, job):
|
||||
if type(job) is not ReadMeshJob or not job.getResult():
|
||||
|
|
@ -624,14 +740,12 @@ 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
|
||||
|
|
|
|||
102
cura/ExtruderManagerModel.py
Normal file
102
cura/ExtruderManagerModel.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# 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
|
||||
import re
|
||||
|
||||
from UM.Application import Application #To get the global container stack to find the current machine.
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerStack import ContainerStack #To create container stacks for each extruder.
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry #Finding containers by ID.
|
||||
|
||||
|
||||
## Class that handles the current extruder stack.
|
||||
#
|
||||
# This finds the extruders that are available for the currently active machine
|
||||
# and makes sure that whenever the machine is swapped, this list is kept up to
|
||||
# date. It also contains and updates the setting stacks for the extruders.
|
||||
class ExtruderManagerModel(QObject):
|
||||
## Registers listeners and such to listen to changes to the extruders.
|
||||
#
|
||||
# \param parent Parent QObject of this model.
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._extruderDefinitions = [] #Extruder definitions for the current machine.
|
||||
self._nozzles = {} #Nozzle instances for each extruder.
|
||||
self._extruderTrains = [] #Container stacks for each of the extruder trains.
|
||||
|
||||
Application.getInstance().getGlobalContainerStack().containersChanged.connect(self._reloadExtruders) #When the current machine changes, we need to reload all extruders belonging to the new machine.
|
||||
|
||||
## (Re)loads all extruders of the currently active machine.
|
||||
#
|
||||
# This looks at the global container stack to see which machine is active.
|
||||
# Then it loads the extruder definitions for that machine and the variants
|
||||
# of those definitions. Then it puts the new extruder definitions in the
|
||||
# appropriate place in the container stacks.
|
||||
def _reloadExtruders(self):
|
||||
self._extruderDefinitions = []
|
||||
self._nozzles = {}
|
||||
self._extruderTrains = []
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack: #No machine has been added yet.
|
||||
return #Then leave them empty!
|
||||
|
||||
#Fill the list of extruder trains.
|
||||
machine = global_container_stack.getBottom()
|
||||
extruder_train_ids = machine.getMetaData("machine_extruder_trains")
|
||||
for extruder_train_id in extruder_train_ids:
|
||||
extruders = ContainerRegistry.getInstance().findDefinitionContainers(id = extruder_train_id) #Should be only 1 definition if IDs are unique, but add the whole list anyway.
|
||||
if not extruders: #Empty list or error.
|
||||
Logger.log("w", "Machine definition %s refers to an extruder train \"%s\", but no such extruder was found.", machine.getId(), extruder_train_id)
|
||||
continue
|
||||
self._extruderDefinitions += extruders
|
||||
|
||||
#Fill the nozzles for each of the extruder trains.
|
||||
for extruder in self._extruderDefinitions:
|
||||
self._nozzles[extruder.id] = []
|
||||
all_nozzles = ContainerRegistry.getInstance().findInstanceContainers(type="nozzle")
|
||||
for nozzle in all_nozzles:
|
||||
extruders = nozzle.getMetaDataEntry("definitions").split(",").strip()
|
||||
for extruder_id in extruders:
|
||||
self._nozzles[extruder_id] = nozzle
|
||||
|
||||
#Create the extruder train container stacks.
|
||||
for extruder in self._extruderDefinitions:
|
||||
self._extruderTrains.append(self._createContainerStack(extruder))
|
||||
|
||||
## Creates a container stack for the specified extruder.
|
||||
#
|
||||
# This fills in the specified extruder as base definition, then a nozzle
|
||||
# that fits in that extruder train, then a material that fits through that
|
||||
# nozzle, then a quality profile that can be used with that material, and
|
||||
# finally an empty user profile.
|
||||
#
|
||||
# \param extruder The extruder to create the container stack for.
|
||||
# \return A container stack with the specified extruder as base.
|
||||
def _createContainerStack(self, extruder):
|
||||
container_stack = ContainerStack(self._uniqueName(extruder))
|
||||
#TODO: Fill the container stack.
|
||||
return container_stack
|
||||
|
||||
## Finds a unique name for an extruder stack.
|
||||
#
|
||||
# \param extruder Extruder to design a name for.
|
||||
# \return A name for an extruder stack that is unique and reasonably
|
||||
# human-readable.
|
||||
def _uniqueName(self, extruder):
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
name = extruder.getName().strip()
|
||||
num_check = re.compile("(.*?)\s*#\d$").match(name)
|
||||
if(num_check): #There is a number in the name.
|
||||
name = num_check.group(1) #Filter out the number.
|
||||
if name == "": #Wait, that deleted everything!
|
||||
name = "Extruder"
|
||||
unique_name = name
|
||||
|
||||
i = 1
|
||||
while(container_registry.findContainers(id = unique_name) or container_registry.findContainers(name = unique_name)): #A container already has this name.
|
||||
i += 1 #Try next numbering.
|
||||
unique_name = "%s #%d" % (name, i) #Fill name like this: "Extruder #2".
|
||||
return unique_name
|
||||
292
cura/MachineManagerModel.py
Normal file
292
cura/MachineManagerModel.py
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
import UM.Settings
|
||||
|
||||
import re
|
||||
|
||||
class MachineManagerModel(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
## 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)
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_machine", "")
|
||||
|
||||
active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
|
||||
|
||||
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()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
|
||||
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)
|
||||
|
||||
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._uniqueMachineName(name, definition.getName())
|
||||
|
||||
new_global_stack = UM.Settings.ContainerStack(name)
|
||||
new_global_stack.addMetaDataEntry("type", "machine")
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack)
|
||||
|
||||
empty_container = UM.Settings.ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
||||
variants = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = definition.id)
|
||||
if variants:
|
||||
new_global_stack.addMetaDataEntry("has_variants", True)
|
||||
|
||||
preferred_variant_id = definitions[0].getMetaDataEntry("preferred_variant")
|
||||
variant_instance_container = empty_container
|
||||
if preferred_variant_id:
|
||||
preferred_variant_id = preferred_variant_id.lower()
|
||||
container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_id)
|
||||
if container:
|
||||
variant_instance_container = container[0]
|
||||
|
||||
if variants and variant_instance_container == empty_container:
|
||||
variant_instance_container = variants[0]
|
||||
|
||||
materials = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition.id)
|
||||
if materials:
|
||||
new_global_stack.addMetaDataEntry("has_materials", True)
|
||||
|
||||
preferred_material_id = definitions[0].getMetaDataEntry("preferred_material")
|
||||
material_instance_container = empty_container
|
||||
if preferred_material_id:
|
||||
preferred_material_id = preferred_material_id.lower()
|
||||
container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = preferred_material_id)
|
||||
if container:
|
||||
material_instance_container = container[0]
|
||||
|
||||
if materials and material_instance_container == empty_container:
|
||||
material_instance_container = materials[0]
|
||||
|
||||
preferred_quality_id = definitions[0].getMetaDataEntry("preferred_quality")
|
||||
quality_instance_container = empty_container
|
||||
if preferred_quality_id:
|
||||
preferred_quality_id = preferred_quality_id.lower()
|
||||
container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = preferred_quality_id)
|
||||
if container:
|
||||
quality_instance_container = container[0]
|
||||
|
||||
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(definitions[0])
|
||||
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)
|
||||
|
||||
Application.getInstance().setGlobalContainerStack(new_global_stack)
|
||||
|
||||
# Create a name that is not empty and unique
|
||||
def _uniqueMachineName(self, name, fallback_name):
|
||||
name = name.strip()
|
||||
num_check = re.compile("(.*?)\s*#\d$").match(name)
|
||||
if(num_check):
|
||||
name = num_check.group(1)
|
||||
if name == "":
|
||||
name = fallback_name
|
||||
unique_name = name
|
||||
i = 1
|
||||
|
||||
#Check both the id and the name, because they may not be the same and it is better if they are both unique
|
||||
while UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = unique_name) or \
|
||||
UM.Settings.ContainerRegistry.getInstance().findContainers(None, name = unique_name):
|
||||
i = i + 1
|
||||
unique_name = "%s #%d" % (name, i)
|
||||
|
||||
return unique_name
|
||||
|
||||
@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._global_container_stack:
|
||||
material = self._global_container_stack.findContainer({"type":"material"})
|
||||
if material:
|
||||
return material.getName()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeMaterialChanged)
|
||||
def activeMaterialId(self):
|
||||
if self._global_container_stack:
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
if material:
|
||||
return material.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityName(self):
|
||||
if self._global_container_stack:
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
if quality:
|
||||
return quality.getName()
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityId(self):
|
||||
if self._global_container_stack:
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
if quality:
|
||||
return quality.getId()
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveMaterial(self, material_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=material_id)
|
||||
if not containers or not self._global_container_stack:
|
||||
return
|
||||
|
||||
old_material = self._global_container_stack.findContainer({"type":"material"})
|
||||
if old_material:
|
||||
material_index = self._global_container_stack.getContainerIndex(old_material)
|
||||
self._global_container_stack.replaceContainer(material_index, containers[0])
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveVariant(self, variant_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=variant_id)
|
||||
if not containers or not self._global_container_stack:
|
||||
return
|
||||
|
||||
old_variant = self._global_container_stack.findContainer({"type": "variant"})
|
||||
if old_variant:
|
||||
variant_index = self._global_container_stack.getContainerIndex(old_variant)
|
||||
self._global_container_stack.replaceContainer(variant_index, containers[0])
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveQuality(self, quality_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id)
|
||||
if not containers or not self._global_container_stack:
|
||||
return
|
||||
|
||||
old_quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
if old_quality:
|
||||
quality_index = self._global_container_stack.getContainerIndex(old_quality)
|
||||
self._global_container_stack.replaceContainer(quality_index, containers[0])
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantName(self):
|
||||
if self._global_container_stack:
|
||||
variant = self._global_container_stack.findContainer({"type": "variant"})
|
||||
if variant:
|
||||
return variant.getName()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantId(self):
|
||||
if self._global_container_stack:
|
||||
variant = self._global_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._uniqueMachineName(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
|
||||
if self._global_container_stack and self._global_container_stack.getId() == machine_id:
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks()
|
||||
if containers:
|
||||
Application.getInstance().setGlobalContainerStack(containers[0])
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(machine_id)
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def hasMaterials(self):
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getMetaDataEntry("has_materials", False)
|
||||
|
||||
return False
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def hasVariants(self):
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getMetaDataEntry("has_variants", False)
|
||||
|
||||
return False
|
||||
|
||||
def createMachineManagerModel(engine, script_engine):
|
||||
return MachineManagerModel()
|
||||
|
|
@ -63,6 +63,6 @@ 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()
|
||||
|
|
|
|||
328
cura/PrinterOutputDevice.py
Normal file
328
cura/PrinterOutputDevice.py
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
from UM.OutputDevice.OutputDevice import OutputDevice
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
||||
from enum import IntEnum # For the connection state tracking.
|
||||
from UM.Logger import Logger
|
||||
|
||||
|
||||
## Printer output device adds extra interface options on top of output device.
|
||||
#
|
||||
# The assumption is made the printer is a FDM printer.
|
||||
#
|
||||
# Note that a number of settings are marked as "final". This is because decorators
|
||||
# are not inherited by children. To fix this we use the private counter part of those
|
||||
# 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):
|
||||
def __init__(self, device_id, parent = None):
|
||||
super().__init__(device_id = device_id, parent = parent)
|
||||
|
||||
self._target_bed_temperature = 0
|
||||
self._bed_temperature = 0
|
||||
self._num_extruders = 1
|
||||
self._hotend_temperatures = [0] * self._num_extruders
|
||||
self._target_hotend_temperatures = [0] * self._num_extruders
|
||||
self._progress = 0
|
||||
self._head_x = 0
|
||||
self._head_y = 0
|
||||
self._head_z = 0
|
||||
self._connection_state = ConnectionState.closed
|
||||
|
||||
def requestWrite(self, node, file_name = None, filter_by_machine = False):
|
||||
raise NotImplementedError("requestWrite needs to be implemented")
|
||||
|
||||
## Signals
|
||||
|
||||
# Signal to be emitted when bed temp is changed
|
||||
bedTemperatureChanged = pyqtSignal()
|
||||
|
||||
# Signal to be emitted when target bed temp is changed
|
||||
targetBedTemperatureChanged = pyqtSignal()
|
||||
|
||||
# Signal when the progress is changed (usually when this output device is printing / sending lots of data)
|
||||
progressChanged = pyqtSignal()
|
||||
|
||||
# Signal to be emitted when hotend temp is changed
|
||||
hotendTemperaturesChanged = pyqtSignal()
|
||||
|
||||
# Signal to be emitted when target hotend temp is changed
|
||||
targetHotendTemperaturesChanged = pyqtSignal()
|
||||
|
||||
# Signal to be emitted when head position is changed (x,y,z)
|
||||
headPositionChanged = pyqtSignal()
|
||||
|
||||
# Signal that is emitted every time connection state is changed.
|
||||
# it also sends it's own device_id (for convenience sake)
|
||||
connectionStateChanged = pyqtSignal(str)
|
||||
|
||||
## Get the bed temperature of the bed (if any)
|
||||
# This function is "final" (do not re-implement)
|
||||
# /sa _getBedTemperature implementation function
|
||||
@pyqtProperty(float, notify = bedTemperatureChanged)
|
||||
def bedTemperature(self):
|
||||
return self._bed_temperature
|
||||
|
||||
## Set the (target) bed temperature
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param temperature new target temperature of the bed (in deg C)
|
||||
# /sa _setTargetBedTemperature implementation function
|
||||
@pyqtSlot(int)
|
||||
def setTargetBedTemperature(self, temperature):
|
||||
self._setTargetBedTemperature(temperature)
|
||||
self._target_bed_temperature = temperature
|
||||
self.targetBedTemperatureChanged.emit()
|
||||
|
||||
## Home the head of the connected printer
|
||||
# This function is "final" (do not re-implement)
|
||||
# /sa _homeHead implementation function
|
||||
@pyqtSlot()
|
||||
def homeHead(self):
|
||||
self._homeHead()
|
||||
|
||||
## Home the head of the connected printer
|
||||
# This is an implementation function and should be overriden by children.
|
||||
def _homeHead(self):
|
||||
Logger.log("w", "_homeHead is not implemented by this output device")
|
||||
|
||||
## Home the bed of the connected printer
|
||||
# This function is "final" (do not re-implement)
|
||||
# /sa _homeBed implementation function
|
||||
@pyqtSlot()
|
||||
def homeBed(self):
|
||||
self._homeBed()
|
||||
|
||||
## Home the bed of the connected printer
|
||||
# This is an implementation function and should be overriden by children.
|
||||
# /sa homeBed
|
||||
def _homeBed(self):
|
||||
Logger.log("w", "_homeBed is not implemented by this output device")
|
||||
|
||||
## Protected setter for the bed temperature of the connected printer (if any).
|
||||
# /parameter temperature Temperature bed needs to go to (in deg celsius)
|
||||
# /sa setTargetBedTemperature
|
||||
def _setTargetBedTemperature(self, temperature):
|
||||
Logger.log("w", "_setTargetBedTemperature is not implemented by this output device")
|
||||
|
||||
## Protected setter for the current bed temperature.
|
||||
# This simply sets the bed temperature, but ensures that a signal is emitted.
|
||||
# /param temperature temperature of the bed.
|
||||
def _setBedTemperature(self, temperature):
|
||||
self._bed_temperature = temperature
|
||||
self.bedTemperatureChanged.emit()
|
||||
|
||||
## Get the target bed temperature if connected printer (if any)
|
||||
@pyqtProperty(int, notify = targetBedTemperatureChanged)
|
||||
def targetBedTemperature(self):
|
||||
return self._target_bed_temperature
|
||||
|
||||
## Set the (target) hotend temperature
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param index the index of the hotend that needs to change temperature
|
||||
# /param temperature The temperature it needs to change to (in deg celsius).
|
||||
# /sa _setTargetHotendTemperature implementation function
|
||||
@pyqtSlot(int, int)
|
||||
def setTargetHotendTemperature(self, index, temperature):
|
||||
self._setTargetHotendTemperature(index, temperature)
|
||||
self._target_hotend_temperatures[index] = temperature
|
||||
self.targetHotendTemperaturesChanged.emit()
|
||||
|
||||
## Implementation function of setTargetHotendTemperature.
|
||||
# /param index Index of the hotend to set the temperature of
|
||||
# /param temperature Temperature to set the hotend to (in deg C)
|
||||
# /sa setTargetHotendTemperature
|
||||
def _setTargetHotendTemperature(self, index, temperature):
|
||||
Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device")
|
||||
|
||||
@pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged)
|
||||
def targetHotendTemperatures(self):
|
||||
return self._target_hotend_temperatures
|
||||
|
||||
@pyqtProperty("QVariantList", notify = hotendTemperaturesChanged)
|
||||
def hotendTemperatures(self):
|
||||
return self._hotend_temperatures
|
||||
|
||||
## Protected setter for the current hotend temperature.
|
||||
# This simply sets the hotend temperature, but ensures that a signal is emitted.
|
||||
# /param index Index of the hotend
|
||||
# /param temperature temperature of the hotend (in deg C)
|
||||
def _setHotendTemperature(self, index, temperature):
|
||||
self._hotend_temperatures[index] = temperature
|
||||
self.hotendTemperaturesChanged.emit()
|
||||
|
||||
## Attempt to establish connection
|
||||
def connect(self):
|
||||
raise NotImplementedError("connect needs to be implemented")
|
||||
|
||||
## Attempt to close the connection
|
||||
def close(self):
|
||||
raise NotImplementedError("close needs to be implemented")
|
||||
|
||||
@pyqtProperty(bool, notify = connectionStateChanged)
|
||||
def connectionState(self):
|
||||
return self._connection_state
|
||||
|
||||
## Set the connection state of this output device.
|
||||
# /param connection_state ConnectionState enum.
|
||||
def setConnectionState(self, connection_state):
|
||||
self._connection_state = connection_state
|
||||
self.connectionStateChanged.emit(self._id)
|
||||
|
||||
## Ensure that close gets called when object is destroyed
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
## Get the x position of the head.
|
||||
# This function is "final" (do not re-implement)
|
||||
@pyqtProperty(float, notify = headPositionChanged)
|
||||
def headX(self):
|
||||
return self._head_x
|
||||
|
||||
## Get the y position of the head.
|
||||
# This function is "final" (do not re-implement)
|
||||
@pyqtProperty(float, notify = headPositionChanged)
|
||||
def headY(self):
|
||||
return self._head_y
|
||||
|
||||
## Get the z position of the head.
|
||||
# In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements.
|
||||
# This function is "final" (do not re-implement)
|
||||
@pyqtProperty(float, notify = headPositionChanged)
|
||||
def headZ(self):
|
||||
return self._head_z
|
||||
|
||||
## Update the saved position of the head
|
||||
# This function should be called when a new position for the head is recieved.
|
||||
def _updateHeadPosition(self, x, y ,z):
|
||||
position_changed = False
|
||||
if self._head_x != x:
|
||||
self._head_x = x
|
||||
position_changed = True
|
||||
if self._head_y != y:
|
||||
self._head_y = y
|
||||
position_changed = True
|
||||
if self._head_z != z:
|
||||
self._head_z = z
|
||||
position_changed = True
|
||||
if position_changed:
|
||||
self.headPositionChanged.emit()
|
||||
|
||||
## Set the position of the head.
|
||||
# In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements.
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param x new x location of the head.
|
||||
# /param y new y location of the head.
|
||||
# /param z new z location of the head.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadPosition implementation function
|
||||
@pyqtSlot("long", "long", "long")
|
||||
@pyqtSlot("long", "long", "long", "long")
|
||||
def setHeadPosition(self, x, y, z, speed = 3000):
|
||||
self._setHeadPosition(x, y , z, speed)
|
||||
|
||||
## Set the X position of the head.
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param x x position head needs to move to.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadx implementation function
|
||||
@pyqtSlot("long")
|
||||
@pyqtSlot("long", "long")
|
||||
def setHeadX(self, x, speed = 3000):
|
||||
self._setHeadX(x, speed)
|
||||
|
||||
## Set the Y position of the head.
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param y y position head needs to move to.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadY implementation function
|
||||
@pyqtSlot("long")
|
||||
@pyqtSlot("long", "long")
|
||||
def setHeadY(self, y, speed = 3000):
|
||||
self._setHeadY(y, speed)
|
||||
|
||||
## Set the Z position of the head.
|
||||
# In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements.
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param z z position head needs to move to.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadZ implementation function
|
||||
@pyqtSlot("long")
|
||||
@pyqtSlot("long", "long")
|
||||
def setHeadZ(self, z, speed = 3000):
|
||||
self._setHeadY(z, speed)
|
||||
|
||||
## Move the head of the printer.
|
||||
# Note that this is a relative move. If you want to move the head to a specific position you can use
|
||||
# setHeadPosition
|
||||
# This function is "final" (do not re-implement)
|
||||
# /param x distance in x to move
|
||||
# /param y distance in y to move
|
||||
# /param z distance in z to move
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _moveHead implementation function
|
||||
@pyqtSlot("long", "long", "long")
|
||||
@pyqtSlot("long", "long", "long", "long")
|
||||
def moveHead(self, x = 0, y = 0, z = 0, speed = 3000):
|
||||
self._moveHead(x, y, z, speed)
|
||||
|
||||
## Implementation function of moveHead.
|
||||
# /param x distance in x to move
|
||||
# /param y distance in y to move
|
||||
# /param z distance in z to move
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa moveHead
|
||||
def _moveHead(self, x, y, z, speed):
|
||||
Logger.log("w", "_moveHead is not implemented by this output device")
|
||||
|
||||
## Implementation function of setHeadPosition.
|
||||
# /param x new x location of the head.
|
||||
# /param y new y location of the head.
|
||||
# /param z new z location of the head.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa setHeadPosition
|
||||
def _setHeadPosition(self, x, y, z, speed):
|
||||
Logger.log("w", "_setHeadPosition is not implemented by this output device")
|
||||
|
||||
## Implementation function of setHeadX.
|
||||
# /param x new x location of the head.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa setHeadX
|
||||
def _setHeadX(self, x, speed):
|
||||
Logger.log("w", "_setHeadX is not implemented by this output device")
|
||||
|
||||
## Implementation function of setHeadY.
|
||||
# /param y new y location of the head.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadY
|
||||
def _setHeadY(self, y, speed):
|
||||
Logger.log("w", "_setHeadY is not implemented by this output device")
|
||||
|
||||
## Implementation function of setHeadZ.
|
||||
# /param z new z location of the head.
|
||||
# /param speed Speed by which it needs to move (in mm/minute)
|
||||
# /sa _setHeadZ
|
||||
def _setHeadZ(self, z, speed):
|
||||
Logger.log("w", "_setHeadZ is not implemented by this output device")
|
||||
|
||||
## Get the progress of any currently active process.
|
||||
# This function is "final" (do not re-implement)
|
||||
# /sa _getProgress
|
||||
# /returns float progress of the process. -1 indicates that there is no process.
|
||||
@pyqtProperty(float, notify = progressChanged)
|
||||
def progress(self):
|
||||
return self._progress
|
||||
|
||||
## Set the progress of any currently active process
|
||||
# /param progress Progress of the process.
|
||||
def setProgress(self, progress):
|
||||
if self._progress != progress:
|
||||
self._progress = progress
|
||||
self.progressChanged.emit()
|
||||
|
||||
|
||||
## The current processing state of the backend.
|
||||
class ConnectionState(IntEnum):
|
||||
closed = 0
|
||||
connecting = 1
|
||||
connected = 2
|
||||
busy = 3
|
||||
error = 4
|
||||
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.")
|
||||
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)
|
||||
31
cura/SettingOverrideDecorator.py
Normal file
31
cura/SettingOverrideDecorator.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
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.
|
||||
class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._stack = ContainerStack(stack_id = id(self))
|
||||
self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
|
||||
self._stack.addContainer(self._instance)
|
||||
|
||||
ContainerRegistry.getInstance().addContainer(self._stack)
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
## Ensure that the next stack is always the global stack.
|
||||
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
|
||||
|
||||
def getStack(self):
|
||||
return self._stack
|
||||
17
cura_app.py
17
cura_app.py
|
|
@ -3,8 +3,25 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
#WORKAROUND: GITHUB-704 GITHUB-708
|
||||
# It looks like setuptools creates a .pth file in
|
||||
# the default /usr/lib which causes the default site-packages
|
||||
# to be inserted into sys.path before PYTHONPATH.
|
||||
# This can cause issues such as having libsip loaded from
|
||||
# the system instead of the one provided with Cura, which causes
|
||||
# incompatibility issues with libArcus
|
||||
if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is used
|
||||
PYTHONPATH = os.environ["PYTHONPATH"].split(os.pathsep) # Get the value, split it..
|
||||
PYTHONPATH.reverse() # and reverse it, because we always insert at 1
|
||||
for PATH in PYTHONPATH: # Now beginning with the last PATH
|
||||
PATH_real = os.path.realpath(PATH) # Making the the path "real"
|
||||
if PATH_real in sys.path: # This should always work, but keep it to be sure..
|
||||
sys.path.remove(PATH_real)
|
||||
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
|
||||
|
||||
def exceptHook(hook_type, value, traceback):
|
||||
import cura.CrashHandler
|
||||
cura.CrashHandler.show(hook_type, value, traceback)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[2.1.0]
|
||||
|
||||
*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.
|
||||
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
|
||||
You now have the freedom to select and manipulate multiple objects at the same time.
|
||||
|
|
|
|||
|
|
@ -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()}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
# 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 cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from . import ProcessSlicedLayersJob
|
||||
|
|
@ -29,6 +29,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__()
|
||||
|
||||
|
|
@ -50,19 +54,20 @@ 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)
|
||||
|
||||
self._profile = None
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
self._onActiveProfileChanged()
|
||||
#Triggers for when to (re)start slicing:
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
#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,40 +75,40 @@ class CuraEngineBackend(Backend):
|
|||
self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage
|
||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._slicing = False
|
||||
self._restart = False
|
||||
self._enabled = True
|
||||
self._always_restart = True
|
||||
self._start_slice_job = None
|
||||
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"]
|
||||
|
||||
def close(self):
|
||||
self._terminate() # Forcefully shutdown the backend.
|
||||
|
||||
## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished.
|
||||
# \param time The amount of time the print will take.
|
||||
# \param material_amount The amount of material the print will use.
|
||||
|
|
@ -112,57 +117,47 @@ 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()
|
||||
|
||||
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
|
||||
job.start()
|
||||
job.finished.connect(self._onStartSliceCompleted)
|
||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||
settings_message = self._socket.createMessage("cura.proto.SettingList")
|
||||
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message, settings_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
|
||||
self._stored_layer_data = []
|
||||
if self._start_slice_job is not None:
|
||||
self._start_slice_job.cancel()
|
||||
|
||||
self.slicingCancelled.emit()
|
||||
self.processingProgress.emit(0)
|
||||
Logger.log("d", "Attempting to kill the engine process")
|
||||
|
|
@ -172,18 +167,52 @@ 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):
|
||||
if job.getError() or job.getResult() != True:
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
# 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() == 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.getSettingsMessage())
|
||||
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
|
||||
|
|
@ -199,62 +228,75 @@ 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):
|
||||
super()._onSocketError(error)
|
||||
if Application.getInstance().isShuttingDown():
|
||||
return
|
||||
|
||||
super()._onSocketError(error)
|
||||
self._terminate()
|
||||
|
||||
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")))
|
||||
|
||||
|
|
@ -262,28 +304,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.
|
||||
|
|
@ -294,11 +349,24 @@ 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._onChanged()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import numpy
|
|||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class ProcessSlicedLayersJob(Job):
|
||||
def __init__(self, layers):
|
||||
super().__init__()
|
||||
|
|
@ -48,7 +49,6 @@ class ProcessSlicedLayersJob(Job):
|
|||
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
object_id_map = {}
|
||||
new_node = SceneNode()
|
||||
|
||||
## Remove old layer data (if any)
|
||||
|
|
@ -62,8 +62,6 @@ class ProcessSlicedLayersJob(Job):
|
|||
self._progress.hide()
|
||||
return
|
||||
|
||||
settings = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
mesh = MeshData()
|
||||
layer_data = LayerData.LayerData()
|
||||
layer_count = len(self._layers)
|
||||
|
|
@ -88,8 +86,8 @@ class ProcessSlicedLayersJob(Job):
|
|||
for p in range(layer.repeatedMessageCount("polygons")):
|
||||
polygon = layer.getRepeatedMessage("polygons", p)
|
||||
|
||||
points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
|
||||
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
|
||||
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
|
||||
# Create a new 3D-array, copy the 2D points over and insert the right height.
|
||||
# This uses manual array creation + copy rather than numpy.insert since this is
|
||||
|
|
@ -124,16 +122,17 @@ class ProcessSlicedLayersJob(Job):
|
|||
self._progress.hide()
|
||||
return
|
||||
|
||||
#Add layerdata decorator to scene node to indicate that the node has layerdata
|
||||
# Add LayerDataDecorator to scene node to indicate that the node has layer data
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot()) #Note: After this we can no longer abort!
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import numpy
|
||||
from string import Formatter
|
||||
import traceback
|
||||
from enum import IntEnum
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
|
|
@ -12,11 +13,19 @@ from UM.Logger import Logger
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
|
||||
class StartJobResult(IntEnum):
|
||||
Finished = 1
|
||||
Error = 2
|
||||
SettingError = 3
|
||||
NothingToSlice = 4
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcod
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
if isinstance(key, str):
|
||||
try:
|
||||
return kwargs[key]
|
||||
|
|
@ -27,86 +36,113 @@ class GcodeStartEndFormatter(Formatter):
|
|||
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
|
||||
return "{" + str(key) + "}"
|
||||
|
||||
## Job class that handles sending the current scene data to CuraEngine
|
||||
## Job class that builds up the message of scene data to send to CuraEngine.
|
||||
class StartSliceJob(Job):
|
||||
def __init__(self, profile, socket):
|
||||
def __init__(self, slice_message, settings_message):
|
||||
super().__init__()
|
||||
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._profile = profile
|
||||
self._socket = socket
|
||||
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
|
||||
|
||||
## Runs the job that initiates the slicing.
|
||||
def run(self):
|
||||
self._scene.acquireLock()
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not stack:
|
||||
self.setResult(StartJobResult.Error)
|
||||
return
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("getLayerData"):
|
||||
node.getParent().removeChild(node)
|
||||
break
|
||||
#Don't slice if there is a setting with an error value.
|
||||
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)
|
||||
self.setResult(StartJobResult.SettingError)
|
||||
return
|
||||
|
||||
object_groups = []
|
||||
if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
|
||||
for node in OneAtATimeIterator(self._scene.getRoot()):
|
||||
Job.yieldThread()
|
||||
|
||||
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 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
|
||||
|
||||
children = node.getAllChildren()
|
||||
children.append(node)
|
||||
for child_node in children:
|
||||
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
|
||||
temp_list.append(child_node)
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
Job.yieldThread()
|
||||
if len(object_groups) == 0:
|
||||
Logger.log("w", "No objects suitable for one at a time found, or no correct order found")
|
||||
else:
|
||||
temp_list = []
|
||||
|
||||
if getattr(node, "_outside_buildarea", False):
|
||||
continue
|
||||
|
||||
children = node.getAllChildren()
|
||||
children.append(node)
|
||||
for child_node in children:
|
||||
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
|
||||
temp_list.append(child_node)
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
||||
if not getattr(node, "_outside_buildarea", False):
|
||||
temp_list.append(node)
|
||||
Job.yieldThread()
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
Job.yieldThread()
|
||||
if len(object_groups) == 0:
|
||||
Logger.log("w", "No objects suitable for one at a time found, or no correct order found")
|
||||
else:
|
||||
temp_list = []
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
||||
if not getattr(node, "_outside_buildarea", False):
|
||||
temp_list.append(node)
|
||||
Job.yieldThread()
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
if not object_groups:
|
||||
self.setResult(StartJobResult.NothingToSlice)
|
||||
return
|
||||
|
||||
self._scene.releaseLock()
|
||||
self._buildGlobalSettingsMessage(stack)
|
||||
|
||||
if not object_groups:
|
||||
return
|
||||
for group in object_groups:
|
||||
group_message = self._slice_message.addRepeatedMessage("object_lists")
|
||||
if group[0].getParent().callDecoration("isGroup"):
|
||||
self._handlePerObjectSettings(group[0].getParent(), group_message)
|
||||
for object in group:
|
||||
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
|
||||
|
||||
self._sendSettings(self._profile)
|
||||
obj = group_message.addRepeatedMessage("objects")
|
||||
obj.id = id(object)
|
||||
verts = numpy.array(mesh_data.getVertices())
|
||||
|
||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||
# Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
|
||||
verts[:, [1, 2]] = verts[:, [2, 1]]
|
||||
verts[:, 1] *= -1
|
||||
|
||||
for group in object_groups:
|
||||
group_message = slice_message.addRepeatedMessage("object_lists")
|
||||
if group[0].getParent().callDecoration("isGroup"):
|
||||
self._handlePerObjectSettings(group[0].getParent(), group_message)
|
||||
for current_object in group:
|
||||
mesh_data = current_object.getMeshData().getTransformed(current_object.getWorldTransformation())
|
||||
obj.vertices = verts
|
||||
|
||||
obj = group_message.addRepeatedMessage("objects")
|
||||
obj.id = id(current_object)
|
||||
self._handlePerObjectSettings(object, obj)
|
||||
|
||||
verts = numpy.array(mesh_data.getVertices())
|
||||
verts[:,[1,2]] = verts[:,[2,1]]
|
||||
verts[:,1] *= -1
|
||||
Job.yieldThread()
|
||||
|
||||
obj.vertices = verts
|
||||
self.setResult(StartJobResult.Finished)
|
||||
|
||||
self._handlePerObjectSettings(current_object, obj)
|
||||
def cancel(self):
|
||||
super().cancel()
|
||||
self._is_cancelled = True
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
Logger.log("d", "Sending data to engine for slicing.")
|
||||
self._socket.sendMessage(slice_message)
|
||||
Logger.log("d", "Sending data to engine is completed")
|
||||
self.setResult(True)
|
||||
def isCancelled(self):
|
||||
return self._is_cancelled
|
||||
|
||||
def _expandGcodeTokens(self, key, value, settings):
|
||||
try:
|
||||
|
|
@ -114,24 +150,30 @@ 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 _sendSettings(self, profile):
|
||||
msg = self._socket.createMessage("cura.proto.SettingList");
|
||||
settings = profile.getAllSettingValues(include_machine = True)
|
||||
start_gcode = settings["machine_start_gcode"]
|
||||
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode
|
||||
settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode
|
||||
for key, value in settings.items():
|
||||
s = msg.addRepeatedMessage("settings")
|
||||
s.name = key
|
||||
if key == "machine_start_gcode" or key == "machine_end_gcode":
|
||||
s.value = self._expandGcodeTokens(key, value, settings)
|
||||
else:
|
||||
s.value = str(value).encode("utf-8")
|
||||
## 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")
|
||||
|
||||
self._socket.sendMessage(msg)
|
||||
start_gcode = settings["machine_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(): #Add all submessages for each individual setting.
|
||||
setting_message = self._settings_message.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:
|
||||
setting_message.value = str(value).encode("utf-8")
|
||||
|
||||
def _handlePerObjectSettings(self, node, message):
|
||||
profile = node.callDecoration("getProfile")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ from UM.Logger import Logger
|
|||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
|
||||
|
||||
## A plugin that reads profile data from Cura profile files.
|
||||
#
|
||||
# It reads a profile from a .curaprofile file, and returns it as a profile
|
||||
# instance.
|
||||
class CuraProfileReader(ProfileReader):
|
||||
## Initialises the cura profile reader.
|
||||
#
|
||||
# This does nothing since the only other function is basically stateless.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
|
@ -24,10 +24,11 @@ class CuraProfileReader(ProfileReader):
|
|||
# not be read or didn't contain a valid profile, \code None \endcode is
|
||||
# returned.
|
||||
def read(self, file_name):
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile.
|
||||
# Create an empty profile.
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
|
||||
serialised = ""
|
||||
try:
|
||||
with open(file_name) as f: #Open file for reading.
|
||||
with open(file_name) as f: # Open file for reading.
|
||||
serialised = f.read()
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
|
|
@ -35,6 +36,7 @@ class CuraProfileReader(ProfileReader):
|
|||
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
except Exception as e: #Parsing error. This is not a (valid) Cura profile then.
|
||||
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
|
||||
return profile
|
||||
|
|
@ -6,6 +6,7 @@ from UM.Logger import Logger
|
|||
from UM.SaveFile import SaveFile
|
||||
from UM.Settings.ProfileWriter import ProfileWriter
|
||||
|
||||
|
||||
## Writes profiles to Cura's own profile format with config files.
|
||||
class CuraProfileWriter(ProfileWriter):
|
||||
## Writes a profile to the specified file path.
|
||||
|
|
@ -17,7 +18,7 @@ class CuraProfileWriter(ProfileWriter):
|
|||
def write(self, path, profile):
|
||||
serialised = profile.serialise()
|
||||
try:
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f: #Open the specified file.
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f: # Open the specified file.
|
||||
f.write(serialised)
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to write profile to %s: %s", path, str(e))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from UM.Settings.ProfileReader import ProfileReader
|
|||
from UM.Logger import Logger
|
||||
import re #Regular expressions for parsing escape characters in the settings.
|
||||
|
||||
|
||||
## A class that reads profile data from g-code files.
|
||||
#
|
||||
# It reads the profile data from g-code files and stores it in a new profile.
|
||||
|
|
@ -47,29 +48,34 @@ class GCodeProfileReader(ProfileReader):
|
|||
prefix = ";SETTING_" + str(GCodeProfileReader.version) + " "
|
||||
prefix_length = len(prefix)
|
||||
|
||||
#Loading all settings from the file. They are all at the end, but Python has no reverse seek any more since Python3. TODO: Consider moving settings to the start?
|
||||
serialised = "" #Will be filled with the serialised profile.
|
||||
# Loading all settings from the file.
|
||||
# They are all at the end, but Python has no reverse seek any more since Python3.
|
||||
# TODO: Consider moving settings to the start?
|
||||
serialised = "" # Will be filled with the serialised profile.
|
||||
try:
|
||||
with open(file_name) as f:
|
||||
for line in f:
|
||||
if line.startswith(prefix):
|
||||
serialised += line[prefix_length : -1] #Remove the prefix and the newline from the line, and add it to the rest.
|
||||
# Remove the prefix and the newline from the line and add it to the rest.
|
||||
serialised += line[prefix_length : -1]
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
#Unescape the serialised profile.
|
||||
# Un-escape the serialised profile.
|
||||
pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
|
||||
serialised = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialised) #Perform the replacement with a regular expression.
|
||||
|
||||
#Apply the changes to the current profile.
|
||||
# Perform the replacement with a regular expression.
|
||||
serialised = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialised)
|
||||
|
||||
# Apply the changes to the current profile.
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
profile.setType(None) #Force type to none so it's correctly added.
|
||||
profile.setType(None) # Force type to none so it's correctly added.
|
||||
profile.setReadOnly(False)
|
||||
profile.setDirty(True)
|
||||
except Exception as e: #Not a valid g-code file.
|
||||
except Exception as e: # Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
return profile
|
||||
|
|
@ -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.
|
||||
#
|
||||
|
|
@ -22,9 +31,9 @@ class GCodeWriter(MeshWriter):
|
|||
# Note that the keys of this dictionary are regex strings. The values are
|
||||
# not.
|
||||
escape_characters = {
|
||||
re.escape("\\"): "\\\\", #The escape character.
|
||||
re.escape("\n"): "\\n", #Newlines. They break off the comment.
|
||||
re.escape("\r"): "\\r" #Carriage return. Windows users may need this for visualisation in their editors.
|
||||
re.escape("\\"): "\\\\", # The escape character.
|
||||
re.escape("\n"): "\\n", # Newlines. They break off the comment.
|
||||
re.escape("\r"): "\\r" # Carriage return. Windows users may need this for visualisation in their editors.
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
|
@ -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,33 +49,41 @@ class GCodeWriter(MeshWriter):
|
|||
if gcode_list:
|
||||
for gcode in gcode_list:
|
||||
stream.write(gcode)
|
||||
profile = self._serialiseProfile(Application.getInstance().getMachineManager().getWorkingProfile()) #Serialise the profile and put them at the end of the file.
|
||||
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):
|
||||
prefix = ";SETTING_" + str(GCodeWriter.version) + " " #The prefix to put before each line.
|
||||
# \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.
|
||||
# Escape characters that have a special meaning in g-code comments.
|
||||
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
|
||||
serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised) #Perform the replacement with a regular expression.
|
||||
# Perform the replacement with a regular expression.
|
||||
serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised)
|
||||
|
||||
#Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
|
||||
# Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
|
||||
result = ""
|
||||
for pos in range(0, len(serialised), 80 - prefix_length): #Lines have 80 characters, so the payload of each line is 80 - prefix.
|
||||
|
||||
# Lines have 80 characters, so the payload of each line is 80 - prefix.
|
||||
for pos in range(0, len(serialised), 80 - prefix_length):
|
||||
result += prefix + serialised[pos : pos + 80 - prefix_length] + "\n"
|
||||
serialised = result
|
||||
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ class LayerView(View):
|
|||
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
|
||||
self._proxy = LayerViewProxy.LayerViewProxy()
|
||||
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
||||
self._max_layers = 10
|
||||
self._current_layer_num = 10
|
||||
self._max_layers = 0
|
||||
self._current_layer_num = 0
|
||||
self._current_layer_mesh = None
|
||||
self._current_layer_jumps = None
|
||||
self._top_layers_job = None
|
||||
|
|
|
|||
|
|
@ -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,75 @@
|
|||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -7,27 +7,20 @@ 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 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
|
||||
IdRole = Qt.UserRole + 1 # ID of the node
|
||||
|
||||
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)
|
||||
|
|
@ -35,7 +28,7 @@ class PerObjectSettingsModel(ListModel):
|
|||
self.setProperty(self.find("id", object_id), "profile", profile_name)
|
||||
|
||||
profile = None
|
||||
if profile_name != "global":
|
||||
'''if profile_name != "global":
|
||||
profile = Application.getInstance().getMachineManager().findProfile(profile_name)
|
||||
|
||||
node = self._scene.findObject(object_id)
|
||||
|
|
@ -45,42 +38,38 @@ class PerObjectSettingsModel(ListModel):
|
|||
node.callDecoration("setProfile", profile)
|
||||
else:
|
||||
if node.getDecorator(ProfileOverrideDecorator):
|
||||
node.removeDecorator(ProfileOverrideDecorator)
|
||||
node.removeDecorator(ProfileOverrideDecorator)'''
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def addSettingOverride(self, object_id, key):
|
||||
def addOverride(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())
|
||||
#if not node.getDecorator(SettingOverrideDecorator):
|
||||
# node.addDecorator(SettingOverrideDecorator())
|
||||
|
||||
node.callDecoration("addSetting", key)
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def removeSettingOverride(self, object_id, key):
|
||||
def removerOverride(self, object_id, key):
|
||||
node = self._scene.findObject(object_id)
|
||||
node.callDecoration("removeSetting", key)
|
||||
|
||||
if len(node.callDecoration("getAllSettings")) == 0:
|
||||
node.removeDecorator(SettingOverrideDecorator)
|
||||
#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)
|
||||
})
|
||||
node_stack = node.callDecoration("getStack")
|
||||
|
||||
if not node_stack:
|
||||
self.appendItem({
|
||||
"id": id(node)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,83 +6,114 @@ 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;
|
||||
Repeater
|
||||
{
|
||||
id: contents
|
||||
height: childrenRect.height;
|
||||
|
||||
Repeater {
|
||||
id: settings;
|
||||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
|
||||
{
|
||||
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
|
||||
}
|
||||
}
|
||||
|
||||
model: UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).settings
|
||||
|
||||
UM.SettingItem {
|
||||
delegate: Row
|
||||
{
|
||||
Loader
|
||||
{
|
||||
width: UM.Theme.getSize("setting").width;
|
||||
height: UM.Theme.getSize("section").height;
|
||||
|
||||
property var definition: model
|
||||
property var settingDefinitionsModel: addedSettingsModel
|
||||
property var propertyProvider: provider
|
||||
|
||||
//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"
|
||||
|
||||
source:
|
||||
{
|
||||
switch(model.type) // TODO: This needs to be fixed properly. Got frustrated with it not working, so this is the patch job!
|
||||
{
|
||||
case "int":
|
||||
return "../../resources/qml/Settings/SettingTextField.qml"
|
||||
case "float":
|
||||
return "../../resources/qml/Settings/SettingTextField.qml"
|
||||
case "enum":
|
||||
return "../../resources/qml/Settings/SettingComboBox.qml"
|
||||
case "bool":
|
||||
return "../../resources/qml/Settings/SettingCheckBox.qml"
|
||||
case "str":
|
||||
return "../../resources/qml/Settings/SettingTextField.qml"
|
||||
case "category":
|
||||
return "../../resources/qml/Settings/SettingCategory.qml"
|
||||
default:
|
||||
return "../../resources/qml/Settings/SettingUnknown.qml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
width: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("setting").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
|
||||
onClicked: addedSettingsModel.setVisible(model.key, false);
|
||||
|
||||
style: UM.Theme.styles.setting_item;
|
||||
|
||||
onItemValueChanged: {
|
||||
settings.model.setSettingValue(model.key, value)
|
||||
}
|
||||
|
||||
Button
|
||||
style: ButtonStyle
|
||||
{
|
||||
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
|
||||
background: Rectangle
|
||||
{
|
||||
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", "state", "validationState" ]
|
||||
storeIndex: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,6 +164,7 @@ Item {
|
|||
id: settingPickDialog
|
||||
|
||||
title: catalog.i18nc("@title:window", "Pick a Setting to Customize")
|
||||
property string labelFilter: ""
|
||||
|
||||
TextField {
|
||||
id: filter;
|
||||
|
|
@ -145,123 +177,62 @@ Item {
|
|||
|
||||
placeholderText: catalog.i18nc("@label:textbox", "Filter...");
|
||||
|
||||
onTextChanged: settingCategoriesModel.filter(text);
|
||||
onTextChanged:
|
||||
{
|
||||
if(text != "")
|
||||
{
|
||||
listview.model.filter = {"global_only": false, "label": "*" + text}
|
||||
}
|
||||
else
|
||||
{
|
||||
listview.model.filter = {"global_only": false}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
{
|
||||
"global_only": false
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,23 +13,16 @@ class PerObjectSettingsTool(Tool):
|
|||
super().__init__()
|
||||
self._model = None
|
||||
|
||||
self.setExposedProperties("Model", "SelectedIndex")
|
||||
self.setExposedProperties("SelectedObjectId","ContainerID")
|
||||
|
||||
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):
|
||||
def getSelectedObjectId(self):
|
||||
try:
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
if selected_object.getParent().callDecoration("isGroup"):
|
||||
|
|
@ -37,8 +30,24 @@ class PerObjectSettingsTool(Tool):
|
|||
except:
|
||||
selected_object = None
|
||||
selected_object_id = id(selected_object)
|
||||
index = self.getModel().find("id", selected_object_id)
|
||||
return index
|
||||
return selected_object_id
|
||||
|
||||
def getContainerID(self):
|
||||
try:
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
if selected_object.getParent().callDecoration("isGroup"):
|
||||
selected_object = selected_object.getParent()
|
||||
try:
|
||||
return selected_object.callDecoration("getStack").getId()
|
||||
except:
|
||||
print(":(")
|
||||
return
|
||||
except:
|
||||
print(":((")
|
||||
return
|
||||
|
||||
def setContainerID(self, value):
|
||||
pass
|
||||
|
||||
def _onPreferenceChanged(self, preference):
|
||||
if preference == "cura/active_mode":
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from PyQt5.QtCore import Qt, pyqtSlot, QUrl
|
|||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
#from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
class SettingOverrideModel(ListModel):
|
||||
KeyRole = Qt.UserRole + 1
|
||||
|
|
@ -29,9 +29,9 @@ class SettingOverrideModel(ListModel):
|
|||
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._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")
|
||||
|
|
@ -53,7 +53,8 @@ class SettingOverrideModel(ListModel):
|
|||
self._decorator.setSettingValue(key, value)
|
||||
|
||||
def _onDecoratorsChanged(self, node):
|
||||
if not self._node.getDecorator(SettingOverrideDecorator):
|
||||
return
|
||||
'''if not self._node.getDecorator(SettingOverrideDecorator):
|
||||
self.clear()
|
||||
return
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ class SettingOverrideModel(ListModel):
|
|||
self._decorator.settingAdded.connect(self._onSettingsChanged)
|
||||
self._decorator.settingRemoved.connect(self._onSettingsChanged)
|
||||
self._decorator.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
self._onSettingsChanged()
|
||||
self._onSettingsChanged()'''
|
||||
|
||||
def _createOptionsModel(self, options):
|
||||
if not options:
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ UM.Dialog
|
|||
Label
|
||||
{
|
||||
//: USB Printing dialog label, %1 is head temperature
|
||||
text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.extruderTemperature)
|
||||
text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.hotendTemperatures[0])
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,25 +11,20 @@ import functools
|
|||
import os.path
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Signal import Signal, SignalEmitter
|
||||
from UM.Logger import Logger
|
||||
from UM.OutputDevice.OutputDevice import OutputDevice
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
|
||||
|
||||
from PyQt5.QtQuick import QQuickView
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt
|
||||
from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
||||
def __init__(self, serial_port, parent = None):
|
||||
QObject.__init__(self, parent)
|
||||
OutputDevice.__init__(self, serial_port)
|
||||
SignalEmitter.__init__(self)
|
||||
#super().__init__(serial_port)
|
||||
class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
def __init__(self, serial_port):
|
||||
super().__init__(serial_port)
|
||||
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
|
||||
self.setShortDescription(catalog.i18nc("@action:button", "Print with USB"))
|
||||
self.setDescription(catalog.i18nc("@info:tooltip", "Print with USB"))
|
||||
|
|
@ -46,18 +41,10 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._end_stop_thread.daemon = True
|
||||
self._poll_endstop = -1
|
||||
|
||||
# Printer is connected
|
||||
self._is_connected = False
|
||||
|
||||
# Printer is in the process of connecting
|
||||
self._is_connecting = False
|
||||
|
||||
# The baud checking is done by sending a number of m105 commands to the printer and waiting for a readable
|
||||
# response. If the baudrate is correct, this should make sense, else we get giberish.
|
||||
self._required_responses_auto_baud = 3
|
||||
|
||||
self._progress = 0
|
||||
|
||||
self._listen_thread = threading.Thread(target=self._listen)
|
||||
self._listen_thread.daemon = True
|
||||
|
||||
|
|
@ -82,24 +69,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
# List of gcode lines to be printed
|
||||
self._gcode = []
|
||||
|
||||
# Number of extruders
|
||||
self._extruder_count = 1
|
||||
|
||||
# Temperatures of all extruders
|
||||
self._extruder_temperatures = [0] * self._extruder_count
|
||||
|
||||
# Target temperatures of all extruders
|
||||
self._target_extruder_temperatures = [0] * self._extruder_count
|
||||
|
||||
#Target temperature of the bed
|
||||
self._target_bed_temperature = 0
|
||||
|
||||
# Temperature of the bed
|
||||
self._bed_temperature = 0
|
||||
|
||||
# Current Z stage location
|
||||
self._current_z = 0
|
||||
|
||||
# Check if endstops are ever pressed (used for first run)
|
||||
self._x_min_endstop_pressed = False
|
||||
self._y_min_endstop_pressed = False
|
||||
self._z_min_endstop_pressed = False
|
||||
|
|
@ -119,40 +89,36 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._control_view = None
|
||||
|
||||
onError = pyqtSignal()
|
||||
progressChanged = pyqtSignal()
|
||||
extruderTemperatureChanged = pyqtSignal()
|
||||
bedTemperatureChanged = pyqtSignal()
|
||||
|
||||
firmwareUpdateComplete = pyqtSignal()
|
||||
|
||||
endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"])
|
||||
|
||||
@pyqtProperty(float, notify = progressChanged)
|
||||
def progress(self):
|
||||
return self._progress
|
||||
def _setTargetBedTemperature(self, temperature):
|
||||
Logger.log("d", "Setting bed temperature to %s", temperature)
|
||||
self._sendCommand("M140 S%s" % temperature)
|
||||
|
||||
@pyqtProperty(float, notify = extruderTemperatureChanged)
|
||||
def extruderTemperature(self):
|
||||
return self._extruder_temperatures[0]
|
||||
def _setTargetHotendTemperature(self, index, temperature):
|
||||
Logger.log("d", "Setting hotend %s temperature to %s", index, temperature)
|
||||
self._sendCommand("M104 T%s S%s" % (index, temperature))
|
||||
|
||||
@pyqtProperty(float, notify = bedTemperatureChanged)
|
||||
def bedTemperature(self):
|
||||
return self._bed_temperature
|
||||
def _setHeadPosition(self, x, y , z, speed):
|
||||
self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed))
|
||||
|
||||
@pyqtProperty(str, notify = onError)
|
||||
def error(self):
|
||||
return self._error_state
|
||||
def _setHeadX(self, x, speed):
|
||||
self._sendCommand("G0 X%s F%s" % (x, speed))
|
||||
|
||||
# TODO: Might need to add check that extruders can not be changed when it started printing or loading these settings from settings object
|
||||
def setNumExtuders(self, num):
|
||||
self._extruder_count = num
|
||||
self._extruder_temperatures = [0] * self._extruder_count
|
||||
self._target_extruder_temperatures = [0] * self._extruder_count
|
||||
def _setHeadY(self, y, speed):
|
||||
self._sendCommand("G0 Y%s F%s" % (y, speed))
|
||||
|
||||
## Is the printer actively printing
|
||||
def isPrinting(self):
|
||||
if not self._is_connected or self._serial is None:
|
||||
return False
|
||||
return self._is_printing
|
||||
def _setHeadZ(self, z, speed):
|
||||
self._sendCommand("G0 Y%s F%s" % (z, speed))
|
||||
|
||||
def _homeHead(self):
|
||||
self._sendCommand("G28")
|
||||
|
||||
def _homeBed(self):
|
||||
self._sendCommand("G28 Z")
|
||||
|
||||
@pyqtSlot()
|
||||
def startPrint(self):
|
||||
|
|
@ -160,10 +126,15 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
gcode_list = getattr( Application.getInstance().getController().getScene(), "gcode_list")
|
||||
self.printGCode(gcode_list)
|
||||
|
||||
def _moveHead(self, x, y, z, speed):
|
||||
self._sendCommand("G91")
|
||||
self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed))
|
||||
self._sendCommand("G90")
|
||||
|
||||
## Start a print based on a g-code.
|
||||
# \param gcode_list List with gcode (strings).
|
||||
def printGCode(self, gcode_list):
|
||||
if self.isPrinting() or not self._is_connected:
|
||||
if self._progress or self._connection_state != ConnectionState.connected:
|
||||
Logger.log("d", "Printer is busy or not connected, aborting print")
|
||||
self.writeError.emit(self)
|
||||
return
|
||||
|
|
@ -172,14 +143,14 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
for layer in gcode_list:
|
||||
self._gcode.extend(layer.split("\n"))
|
||||
|
||||
#Reset line number. If this is not done, first line is sometimes ignored
|
||||
# Reset line number. If this is not done, first line is sometimes ignored
|
||||
self._gcode.insert(0, "M110")
|
||||
self._gcode_position = 0
|
||||
self._print_start_time_100 = None
|
||||
self._is_printing = True
|
||||
self._print_start_time = time.time()
|
||||
|
||||
for i in range(0, 4): #Push first 4 entries before accepting other inputs
|
||||
for i in range(0, 4): # Push first 4 entries before accepting other inputs
|
||||
self._sendNextGcodeLine()
|
||||
|
||||
self.writeFinished.emit(self)
|
||||
|
|
@ -194,11 +165,11 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
if not self._updating_firmware and not self._connect_thread.isAlive():
|
||||
self._connect_thread.start()
|
||||
|
||||
## Private fuction (threaded) that actually uploads the firmware.
|
||||
## Private function (threaded) that actually uploads the firmware.
|
||||
def _updateFirmware(self):
|
||||
self.setProgress(0, 100)
|
||||
|
||||
if self._is_connecting or self._is_connected:
|
||||
if self._connection_state != ConnectionState.closed:
|
||||
self.close()
|
||||
hex_file = intelHex.readHex(self._firmware_file_name)
|
||||
|
||||
|
|
@ -214,7 +185,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
time.sleep(1) # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
|
||||
# Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
|
||||
time.sleep(1)
|
||||
|
||||
if not programmer.isConnected():
|
||||
Logger.log("e", "Unable to connect with serial. Could not update firmware")
|
||||
|
|
@ -253,14 +225,14 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._poll_endstop = False
|
||||
|
||||
def _pollEndStop(self):
|
||||
while self._is_connected and self._poll_endstop:
|
||||
while self._connection_state == ConnectionState.connected and self._poll_endstop:
|
||||
self.sendCommand("M119")
|
||||
time.sleep(0.5)
|
||||
|
||||
## Private connect function run by thread. Can be started by calling connect.
|
||||
def _connect(self):
|
||||
Logger.log("d", "Attempting to connect to %s", self._serial_port)
|
||||
self._is_connecting = True
|
||||
self.setConnectionState(ConnectionState.connecting)
|
||||
programmer = stk500v2.Stk500v2()
|
||||
try:
|
||||
programmer.connect(self._serial_port) # Connect with the serial, if this succeeds, it's an arduino based usb device.
|
||||
|
|
@ -270,14 +242,15 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
except Exception as e:
|
||||
Logger.log("i", "Could not establish connection on %s, unknown reasons. Device is not arduino based." % self._serial_port)
|
||||
|
||||
# If the programmer connected, we know its an atmega based version. Not all that useful, but it does give some debugging information.
|
||||
# If the programmer connected, we know its an atmega based version.
|
||||
# Not all that useful, but it does give some debugging information.
|
||||
for baud_rate in self._getBaudrateList(): # Cycle all baud rates (auto detect)
|
||||
Logger.log("d","Attempting to connect to printer with serial %s on baud rate %s", self._serial_port, baud_rate)
|
||||
if self._serial is None:
|
||||
try:
|
||||
self._serial = serial.Serial(str(self._serial_port), baud_rate, timeout = 3, writeTimeout = 10000)
|
||||
except serial.SerialException:
|
||||
#Logger.log("i", "Could not open port %s" % self._serial_port)
|
||||
Logger.log("d", "Could not open port %s" % self._serial_port)
|
||||
continue
|
||||
else:
|
||||
if not self.setBaudRate(baud_rate):
|
||||
|
|
@ -291,23 +264,25 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
while timeout_time > time.time():
|
||||
line = self._readline()
|
||||
if line is None:
|
||||
self.setIsConnected(False) # Something went wrong with reading, could be that close was called.
|
||||
# Something went wrong with reading, could be that close was called.
|
||||
self.setConnectionState(ConnectionState.closed)
|
||||
return
|
||||
|
||||
if b"T:" in line:
|
||||
self._serial.timeout = 0.5
|
||||
sucesfull_responses += 1
|
||||
if sucesfull_responses >= self._required_responses_auto_baud:
|
||||
self._serial.timeout = 2 #Reset serial timeout
|
||||
self.setIsConnected(True)
|
||||
self._serial.timeout = 2 # Reset serial timeout
|
||||
self.setConnectionState(ConnectionState.connected)
|
||||
self._listen_thread.start() # Start listening
|
||||
Logger.log("i", "Established printer connection on port %s" % self._serial_port)
|
||||
return
|
||||
|
||||
self._sendCommand("M105") # Send M105 as long as we are listening, otherwise we end up in an undefined state
|
||||
self._sendCommand("M105") # Send M105 as long as we are listening, otherwise we end up in an undefined state
|
||||
|
||||
Logger.log("e", "Baud rate detection for %s failed", self._serial_port)
|
||||
self.close() # Unable to connect, wrap up.
|
||||
self.setIsConnected(False)
|
||||
self.close() # Unable to connect, wrap up.
|
||||
self.setConnectionState(ConnectionState.closed)
|
||||
|
||||
## Set the baud rate of the serial. This can cause exceptions, but we simply want to ignore those.
|
||||
def setBaudRate(self, baud_rate):
|
||||
|
|
@ -317,21 +292,9 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
except Exception as e:
|
||||
return False
|
||||
|
||||
def setIsConnected(self, state):
|
||||
self._is_connecting = False
|
||||
if self._is_connected != state:
|
||||
self._is_connected = state
|
||||
self.connectionStateChanged.emit(self._serial_port)
|
||||
if self._is_connected:
|
||||
self._listen_thread.start() #Start listening
|
||||
else:
|
||||
Logger.log("w", "Printer connection state was not changed")
|
||||
|
||||
connectionStateChanged = Signal()
|
||||
|
||||
## Close the printer connection
|
||||
def close(self):
|
||||
Logger.log("d", "Closing the printer connection.")
|
||||
Logger.log("d", "Closing the USB printer connection.")
|
||||
if self._connect_thread.isAlive():
|
||||
try:
|
||||
self._connect_thread.join()
|
||||
|
|
@ -339,10 +302,10 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
Logger.log("d", "PrinterConnection.close: %s (expected)", e)
|
||||
pass # This should work, but it does fail sometimes for some reason
|
||||
|
||||
self._connect_thread = threading.Thread(target=self._connect)
|
||||
self._connect_thread = threading.Thread(target = self._connect)
|
||||
self._connect_thread.daemon = True
|
||||
|
||||
self.setIsConnected(False)
|
||||
self.setConnectionState(ConnectionState.closed)
|
||||
if self._serial is not None:
|
||||
try:
|
||||
self._listen_thread.join()
|
||||
|
|
@ -350,50 +313,10 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
pass
|
||||
self._serial.close()
|
||||
|
||||
self._listen_thread = threading.Thread(target=self._listen)
|
||||
self._listen_thread = threading.Thread(target = self._listen)
|
||||
self._listen_thread.daemon = True
|
||||
self._serial = None
|
||||
|
||||
def isConnected(self):
|
||||
return self._is_connected
|
||||
|
||||
@pyqtSlot(int)
|
||||
def heatupNozzle(self, temperature):
|
||||
Logger.log("d", "Setting nozzle temperature to %s", temperature)
|
||||
self._sendCommand("M104 S%s" % temperature)
|
||||
|
||||
@pyqtSlot(int)
|
||||
def heatupBed(self, temperature):
|
||||
Logger.log("d", "Setting bed temperature to %s", temperature)
|
||||
self._sendCommand("M140 S%s" % temperature)
|
||||
|
||||
@pyqtSlot()
|
||||
def setMoveToRelative(self):
|
||||
self._sendCommand("G91")
|
||||
|
||||
@pyqtSlot()
|
||||
def setMoveToAbsolute(self):
|
||||
self._sendCommand("G90")
|
||||
|
||||
@pyqtSlot("long", "long","long")
|
||||
def moveHead(self, x, y, z):
|
||||
Logger.log("d","Moving head to %s, %s , %s", x, y, z)
|
||||
self._sendCommand("G0 X%s Y%s Z%s F3000" % (x, y, z))
|
||||
|
||||
@pyqtSlot("long", "long","long")
|
||||
def moveHeadRelative(self, x, y, z):
|
||||
self.setMoveToRelative()
|
||||
self.moveHead(x,y,z)
|
||||
self.setMoveToAbsolute()
|
||||
|
||||
@pyqtSlot()
|
||||
def homeHead(self):
|
||||
self._sendCommand("G28")
|
||||
|
||||
@pyqtSlot()
|
||||
def homeBed(self):
|
||||
self._sendCommand("G28 Z")
|
||||
|
||||
## Directly send the command, withouth checking connection state (eg; printing).
|
||||
# \param cmd string with g-code
|
||||
def _sendCommand(self, cmd):
|
||||
|
|
@ -402,19 +325,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
|
||||
if "M109" in cmd or "M190" in cmd:
|
||||
self._heatup_wait_start_time = time.time()
|
||||
if "M104" in cmd or "M109" in cmd:
|
||||
try:
|
||||
t = 0
|
||||
if "T" in cmd:
|
||||
t = int(re.search("T([0-9]+)", cmd).group(1))
|
||||
self._target_extruder_temperatures[t] = float(re.search("S([0-9]+)", cmd).group(1))
|
||||
except:
|
||||
pass
|
||||
if "M140" in cmd or "M190" in cmd:
|
||||
try:
|
||||
self._target_bed_temperature = float(re.search("S([0-9]+)", cmd).group(1))
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
command = (cmd + "\n").encode()
|
||||
self._serial.write(b"\n")
|
||||
|
|
@ -433,10 +344,6 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._setErrorState("Unexpected error while writing serial port %s " % e)
|
||||
self.close()
|
||||
|
||||
## Ensure that close gets called when object is destroyed
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def createControlInterface(self):
|
||||
if self._control_view is None:
|
||||
Logger.log("d", "Creating control interface for printer connection")
|
||||
|
|
@ -456,9 +363,9 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
## Send a command to printer.
|
||||
# \param cmd string with g-code
|
||||
def sendCommand(self, cmd):
|
||||
if self.isPrinting():
|
||||
if self._progress:
|
||||
self._command_queue.put(cmd)
|
||||
elif self.isConnected():
|
||||
elif self._connection_state == ConnectionState.connected:
|
||||
self._sendCommand(cmd)
|
||||
|
||||
## Set the error state with a message.
|
||||
|
|
@ -467,24 +374,6 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._error_state = error
|
||||
self.onError.emit()
|
||||
|
||||
## Private function to set the temperature of an extruder
|
||||
# \param index index of the extruder
|
||||
# \param temperature received temperature
|
||||
def _setExtruderTemperature(self, index, temperature):
|
||||
try:
|
||||
self._extruder_temperatures[index] = temperature
|
||||
self.extruderTemperatureChanged.emit()
|
||||
except Exception as e:
|
||||
Logger.log("d", "PrinterConnection._setExtruderTemperature: ", e)
|
||||
pass
|
||||
|
||||
## Private function to set the temperature of the bed.
|
||||
# As all printers (as of time of writing) only support a single heated bed,
|
||||
# these are not indexed as with extruders.
|
||||
def _setBedTemperature(self, temperature):
|
||||
self._bed_temperature = temperature
|
||||
self.bedTemperatureChanged.emit()
|
||||
|
||||
def requestWrite(self, node, file_name = None, filter_by_machine = False):
|
||||
self.showControlInterface()
|
||||
|
||||
|
|
@ -507,15 +396,14 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port)
|
||||
temperature_request_timeout = time.time()
|
||||
ok_timeout = time.time()
|
||||
while self._is_connected:
|
||||
while self._connection_state == ConnectionState.connected:
|
||||
line = self._readline()
|
||||
|
||||
if line is None:
|
||||
break # None is only returned when something went wrong. Stop listening
|
||||
break # None is only returned when something went wrong. Stop listening
|
||||
|
||||
if time.time() > temperature_request_timeout:
|
||||
if self._extruder_count > 0:
|
||||
self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._extruder_count
|
||||
if self._num_extruders > 0:
|
||||
self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders
|
||||
self.sendCommand("M105 T%d" % (self._temperature_requested_extruder_index))
|
||||
else:
|
||||
self.sendCommand("M105")
|
||||
|
|
@ -524,8 +412,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
if line.startswith(b"Error:"):
|
||||
# Oh YEAH, consistency.
|
||||
# Marlin reports a MIN/MAX temp error as "Error:x\n: Extruder switched off. MAXTEMP triggered !\n"
|
||||
# But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!"
|
||||
# So we can have an extra newline in the most common case. Awesome work people.
|
||||
# But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!"
|
||||
# So we can have an extra newline in the most common case. Awesome work people.
|
||||
if re.match(b"Error:[0-9]\n", line):
|
||||
line = line.rstrip() + self._readline()
|
||||
|
||||
|
|
@ -534,12 +422,12 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
if not self.hasError():
|
||||
self._setErrorState(line[6:])
|
||||
|
||||
elif b" T:" in line or line.startswith(b"T:"): #Temperature message
|
||||
elif b" T:" in line or line.startswith(b"T:"): # Temperature message
|
||||
try:
|
||||
self._setExtruderTemperature(self._temperature_requested_extruder_index,float(re.search(b"T: *([0-9\.]*)", line).group(1)))
|
||||
self._setHotendTemperature(self._temperature_requested_extruder_index, float(re.search(b"T: *([0-9\.]*)", line).group(1)))
|
||||
except:
|
||||
pass
|
||||
if b"B:" in line: # Check if it's a bed temperature
|
||||
if b"B:" in line: # Check if it's a bed temperature
|
||||
try:
|
||||
self._setBedTemperature(float(re.search(b"B: *([0-9\.]*)", line).group(1)))
|
||||
except Exception as e:
|
||||
|
|
@ -551,7 +439,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
|
||||
if self._is_printing:
|
||||
if line == b"" and time.time() > ok_timeout:
|
||||
line = b"ok" # Force a timeout (basicly, send next command)
|
||||
line = b"ok" # Force a timeout (basically, send next command)
|
||||
|
||||
if b"ok" in line:
|
||||
ok_timeout = time.time() + 5
|
||||
|
|
@ -559,17 +447,17 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
self._sendCommand(self._command_queue.get())
|
||||
else:
|
||||
self._sendNextGcodeLine()
|
||||
elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
|
||||
elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
|
||||
try:
|
||||
self._gcode_position = int(line.replace(b"N:",b" ").replace(b"N",b" ").replace(b":",b" ").split()[-1])
|
||||
except:
|
||||
if b"rs" in line:
|
||||
self._gcode_position = int(line.split()[1])
|
||||
|
||||
else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
|
||||
else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
|
||||
if line == b"":
|
||||
if self._extruder_count > 0:
|
||||
self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._extruder_count
|
||||
if self._num_extruders > 0:
|
||||
self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders
|
||||
self.sendCommand("M105 T%d" % self._temperature_requested_extruder_index)
|
||||
else:
|
||||
self.sendCommand("M105")
|
||||
|
|
@ -588,7 +476,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
line = line.strip()
|
||||
try:
|
||||
if line == "M0" or line == "M1":
|
||||
line = "M105" #Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||
line = "M105" # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||
if ("G0" in line or "G1" in line) and "Z" in line:
|
||||
z = float(re.search("Z([0-9\.]*)", line).group(1))
|
||||
if self._current_z != z:
|
||||
|
|
@ -600,13 +488,13 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
|
||||
self._sendCommand("N%d%s*%d" % (self._gcode_position, line, checksum))
|
||||
self._gcode_position += 1
|
||||
self.setProgress(( self._gcode_position / len(self._gcode)) * 100)
|
||||
self.setProgress((self._gcode_position / len(self._gcode)) * 100)
|
||||
self.progressChanged.emit()
|
||||
|
||||
## Set the progress of the print.
|
||||
# It will be normalized (based on max_progress) to range 0 - 100
|
||||
def setProgress(self, progress, max_progress = 100):
|
||||
self._progress = (progress / max_progress) * 100 #Convert to scale of 0-100
|
||||
self._progress = (progress / max_progress) * 100 # Convert to scale of 0-100
|
||||
self.progressChanged.emit()
|
||||
|
||||
## Cancel the current print. Printer connection wil continue to listen.
|
||||
|
|
@ -623,7 +511,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
|
||||
## Check if the process did not encounter an error yet.
|
||||
def hasError(self):
|
||||
return self._error_state != None
|
||||
return self._error_state is not None
|
||||
|
||||
## private read line used by printer connection to listen for data on serial port.
|
||||
def _readline(self):
|
||||
|
|
@ -632,7 +520,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
try:
|
||||
ret = self._serial.readline()
|
||||
except Exception as e:
|
||||
Logger.log("e","Unexpected error while reading serial port. %s" %e)
|
||||
Logger.log("e", "Unexpected error while reading serial port. %s" % e)
|
||||
self._setErrorState("Printer has been disconnected")
|
||||
self.close()
|
||||
return None
|
||||
|
|
@ -646,7 +534,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
|
|||
|
||||
def _onFirmwareUpdateComplete(self):
|
||||
self._update_firmware_thread.join()
|
||||
self._update_firmware_thread = threading.Thread(target= self._updateFirmware)
|
||||
self._update_firmware_thread = threading.Thread(target = self._updateFirmware)
|
||||
self._update_firmware_thread.daemon = True
|
||||
|
||||
self.connect()
|
||||
|
|
@ -2,12 +2,13 @@
|
|||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Signal import Signal, SignalEmitter
|
||||
from . import PrinterConnection
|
||||
from . import USBPrinterOutputDevice
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
||||
from cura.PrinterOutputDevice import ConnectionState
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Message import Message
|
||||
|
||||
|
|
@ -20,21 +21,19 @@ import time
|
|||
import os.path
|
||||
from UM.Extension import Extension
|
||||
|
||||
from PyQt5.QtQuick import QQuickView
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
||||
|
||||
## Manager class that ensures that a usbPrinteroutput device is created for every connected USB printer.
|
||||
class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
||||
def __init__(self, parent = None):
|
||||
QObject.__init__(self, parent)
|
||||
SignalEmitter.__init__(self)
|
||||
OutputDevicePlugin.__init__(self)
|
||||
Extension.__init__(self)
|
||||
super().__init__(parent = parent)
|
||||
self._serial_port_list = []
|
||||
self._printer_connections = {}
|
||||
self._printer_connections_model = None
|
||||
self._usb_output_devices = {}
|
||||
self._usb_output_devices_model = None
|
||||
self._update_thread = threading.Thread(target = self._updateThread)
|
||||
self._update_thread.setDaemon(True)
|
||||
|
||||
|
|
@ -46,20 +45,20 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Update Firmware"), self.updateAllFirmware)
|
||||
|
||||
Application.getInstance().applicationShuttingDown.connect(self.stop)
|
||||
self.addConnectionSignal.connect(self.addConnection) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
||||
self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
||||
|
||||
addConnectionSignal = Signal()
|
||||
printerConnectionStateChanged = pyqtSignal()
|
||||
addUSBOutputDeviceSignal = Signal()
|
||||
connectionStateChanged = pyqtSignal()
|
||||
|
||||
progressChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(float, notify = progressChanged)
|
||||
def progress(self):
|
||||
progress = 0
|
||||
for printer_name, connection in self._printer_connections.items(): # TODO: @UnusedVariable "printer_name"
|
||||
progress += connection.progress
|
||||
for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
|
||||
progress += device.progress
|
||||
|
||||
return progress / len(self._printer_connections)
|
||||
return progress / len(self._usb_output_devices)
|
||||
|
||||
def start(self):
|
||||
self._check_updates = True
|
||||
|
|
@ -93,25 +92,25 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
|
||||
@pyqtSlot()
|
||||
def updateAllFirmware(self):
|
||||
if not self._printer_connections:
|
||||
if not self._usb_output_devices:
|
||||
Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show()
|
||||
return
|
||||
|
||||
self.spawnFirmwareInterface("")
|
||||
for printer_connection in self._printer_connections:
|
||||
for printer_connection in self._usb_output_devices:
|
||||
try:
|
||||
self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
except FileNotFoundError:
|
||||
self._printer_connections[printer_connection].setProgress(100, 100)
|
||||
self._usb_output_devices[printer_connection].setProgress(100, 100)
|
||||
Logger.log("w", "No firmware found for printer %s", printer_connection)
|
||||
continue
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
def updateFirmwareBySerial(self, serial_port):
|
||||
if serial_port in self._printer_connections:
|
||||
self.spawnFirmwareInterface(self._printer_connections[serial_port].getSerialPort())
|
||||
if serial_port in self._usb_output_devices:
|
||||
self.spawnFirmwareInterface(self._usb_output_devices[serial_port].getSerialPort())
|
||||
try:
|
||||
self._printer_connections[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
except FileNotFoundError:
|
||||
self._firmware_view.close()
|
||||
Logger.log("e", "Could not find firmware required for this machine")
|
||||
|
|
@ -123,10 +122,10 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
@classmethod
|
||||
def getInstance(cls, engine = None, script_engine = None):
|
||||
# Note: Explicit use of class name to prevent issues with inheritance.
|
||||
if USBPrinterManager._instance is None:
|
||||
USBPrinterManager._instance = cls()
|
||||
if USBPrinterOutputDeviceManager._instance is None:
|
||||
USBPrinterOutputDeviceManager._instance = cls()
|
||||
|
||||
return USBPrinterManager._instance
|
||||
return USBPrinterOutputDeviceManager._instance
|
||||
|
||||
def _getDefaultFirmwareName(self):
|
||||
machine_instance = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
|
|
@ -141,6 +140,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
# The *.hex files are stored at a seperate repository:
|
||||
# https://github.com/Ultimaker/cura-binary-data/tree/master/cura/resources/firmware
|
||||
machine_without_extras = {"bq_witbox" : "MarlinWitbox.hex",
|
||||
"bq_hephestos_2" : "MarlinHephestos2.hex",
|
||||
"ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
|
||||
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
|
||||
"ultimaker2" : "MarlinUltimaker2.hex",
|
||||
|
|
@ -154,13 +154,13 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
|
||||
##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"):
|
||||
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
|
||||
hex_file = machine_with_heated_bed[machine_type] # 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
|
||||
hex_file = machine_without_extras[machine_type] # Return "basic" firmware
|
||||
else:
|
||||
Logger.log("e", "There is no firmware for machine %s.", machine_type)
|
||||
|
||||
|
|
@ -170,48 +170,53 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
|||
Logger.log("e", "Could not find any firmware for machine %s.", machine_type)
|
||||
raise FileNotFoundError()
|
||||
|
||||
## Helper to identify serial ports (and scan for them)
|
||||
def _addRemovePorts(self, serial_ports):
|
||||
# First, find and add all new or changed keys
|
||||
for serial_port in list(serial_ports):
|
||||
if serial_port not in self._serial_port_list:
|
||||
self.addConnectionSignal.emit(serial_port) #Hack to ensure its created in main thread
|
||||
self.addUSBOutputDeviceSignal.emit(serial_port) # Hack to ensure its created in main thread
|
||||
continue
|
||||
self._serial_port_list = list(serial_ports)
|
||||
|
||||
connections_to_remove = []
|
||||
for port, connection in self._printer_connections.items():
|
||||
devices_to_remove = []
|
||||
for port, device in self._usb_output_devices.items():
|
||||
if port not in self._serial_port_list:
|
||||
connection.close()
|
||||
connections_to_remove.append(port)
|
||||
|
||||
for port in connections_to_remove:
|
||||
del self._printer_connections[port]
|
||||
device.close()
|
||||
devices_to_remove.append(port)
|
||||
|
||||
for port in devices_to_remove:
|
||||
del self._usb_output_devices[port]
|
||||
|
||||
## Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
||||
def addConnection(self, serial_port):
|
||||
connection = PrinterConnection.PrinterConnection(serial_port)
|
||||
connection.connect()
|
||||
connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
|
||||
connection.progressChanged.connect(self.progressChanged)
|
||||
self._printer_connections[serial_port] = connection
|
||||
def addOutputDevice(self, serial_port):
|
||||
device = USBPrinterOutputDevice.USBPrinterOutputDevice(serial_port)
|
||||
device.connectionStateChanged.connect(self._onConnectionStateChanged)
|
||||
device.connect()
|
||||
device.progressChanged.connect(self.progressChanged)
|
||||
self._usb_output_devices[serial_port] = device
|
||||
|
||||
def _onPrinterConnectionStateChanged(self, serial_port):
|
||||
if self._printer_connections[serial_port].isConnected():
|
||||
self.getOutputDeviceManager().addOutputDevice(self._printer_connections[serial_port])
|
||||
else:
|
||||
self.getOutputDeviceManager().removeOutputDevice(serial_port)
|
||||
self.printerConnectionStateChanged.emit()
|
||||
## If one of the states of the connected devices change, we might need to add / remove them from the global list.
|
||||
def _onConnectionStateChanged(self, serial_port):
|
||||
try:
|
||||
if self._usb_output_devices[serial_port].connectionState == ConnectionState.connected:
|
||||
self.getOutputDeviceManager().addOutputDevice(self._usb_output_devices[serial_port])
|
||||
else:
|
||||
self.getOutputDeviceManager().removeOutputDevice(serial_port)
|
||||
self.connectionStateChanged.emit()
|
||||
except KeyError:
|
||||
pass # no output device by this device_id found in connection list.
|
||||
|
||||
@pyqtProperty(QObject , notify = printerConnectionStateChanged)
|
||||
|
||||
@pyqtProperty(QObject , notify = connectionStateChanged)
|
||||
def connectedPrinterList(self):
|
||||
self._printer_connections_model = ListModel()
|
||||
self._printer_connections_model.addRoleName(Qt.UserRole + 1,"name")
|
||||
self._printer_connections_model.addRoleName(Qt.UserRole + 2, "printer")
|
||||
for connection in self._printer_connections:
|
||||
if self._printer_connections[connection].isConnected():
|
||||
self._printer_connections_model.appendItem({"name":connection, "printer": self._printer_connections[connection]})
|
||||
return self._printer_connections_model
|
||||
self._usb_output_devices_model = ListModel()
|
||||
self._usb_output_devices_model.addRoleName(Qt.UserRole + 1, "name")
|
||||
self._usb_output_devices_model.addRoleName(Qt.UserRole + 2, "printer")
|
||||
for connection in self._usb_output_devices:
|
||||
if self._usb_output_devices[connection].connectionState == ConnectionState.connected:
|
||||
self._usb_output_devices_model.appendItem({"name": connection, "printer": self._usb_output_devices[connection]})
|
||||
return self._usb_output_devices_model
|
||||
|
||||
## Create a list of serial ports on the system.
|
||||
# \param only_list_usb If true, only usb ports are listed
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import USBPrinterManager
|
||||
from . import USBPrinterOutputDeviceManager
|
||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
|
@ -19,5 +19,7 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
qmlRegisterSingletonType(USBPrinterManager.USBPrinterManager, "UM", 1, 0, "USBPrinterManager", USBPrinterManager.USBPrinterManager.getInstance)
|
||||
return {"extension":USBPrinterManager.USBPrinterManager.getInstance(),"output_device": USBPrinterManager.USBPrinterManager.getInstance() }
|
||||
# We are violating the QT API here (as we use a factory, which is technically not allowed).
|
||||
# but we don't really have another means for doing this (and it seems to you know -work-)
|
||||
qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance)
|
||||
return {"extension":USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance(), "output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
137
plugins/XmlMaterialProfile/XmlMaterialProfile.py
Normal file
137
plugins/XmlMaterialProfile/XmlMaterialProfile.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# 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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
new_material = XmlMaterialProfile(self.id + "_" + machine_id)
|
||||
new_material.setName(self.getName())
|
||||
new_material.setMetaData(self.getMetaData())
|
||||
new_material.setDefinition(definitions[0])
|
||||
|
||||
for key, value in global_setting_values.items():
|
||||
new_material.setProperty(key, "value", value, definitions[0])
|
||||
|
||||
for key, value in machine_setting_values.items():
|
||||
new_material.setProperty(key, "value", value, definitions[0])
|
||||
|
||||
new_material._dirty = False
|
||||
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)
|
||||
|
||||
|
||||
__material_property_setting_map = {
|
||||
"print temperature": "material_print_temperature",
|
||||
"heated bed temperature": "material_bed_temperature",
|
||||
"standby temperature": "material_standby_temperature",
|
||||
}
|
||||
|
||||
__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") }
|
||||
|
||||
93
resources/definitions/bq_hephestos.def.json
Normal file
93
resources/definitions/bq_hephestos.def.json
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"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": {
|
||||
"value": [
|
||||
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/sec\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": { "value": [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 }
|
||||
}
|
||||
}
|
||||
93
resources/definitions/bq_hephestos_xl.def.json
Normal file
93
resources/definitions/bq_hephestos_xl.def.json
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"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": {
|
||||
"value": [
|
||||
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/sec\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
|
||||
}
|
||||
}
|
||||
}
|
||||
94
resources/definitions/bq_witbox.def.json
Normal file
94
resources/definitions/bq_witbox.def.json
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"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": {
|
||||
"value": [
|
||||
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/sec\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
|
||||
}
|
||||
}
|
||||
}
|
||||
118
resources/definitions/fdmextruder.def.json
Normal file
118
resources/definitions/fdmextruder.def.json
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
{
|
||||
"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": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"machine_extruder_start_code":
|
||||
{
|
||||
"label": "Extruder Start G-Code",
|
||||
"description": "Start g-code to execute whenever turning the extruder on.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"machine_extruder_end_code":
|
||||
{
|
||||
"label": "Extruder End G-Code",
|
||||
"description": "End g-code to execute whenever turning the extruder off.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
},
|
||||
"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,
|
||||
"global_only": "True"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2810
resources/definitions/fdmprinter.def.json
Normal file
2810
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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\""
|
||||
}
|
||||
}
|
||||
}
|
||||
14
resources/definitions/ultimaker.def.json
Normal file
14
resources/definitions/ultimaker.def.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "ultimaker_base",
|
||||
"version": 2,
|
||||
"visible": false,
|
||||
"name": "Ultimaker",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"preferred_profile": "Normal Quality",
|
||||
"preferred_nozzle": "0.4 mm",
|
||||
"preferred_material": "PLA"
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
30
resources/definitions/ultimaker2_extended_plus.def.json
Normal file
30
resources/definitions/ultimaker2_extended_plus.def.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"id": "ultimaker2_extended_plus",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2 Extended+",
|
||||
"inherits": "ultimaker2_plus",
|
||||
"visible": false,
|
||||
"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]]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
74
resources/definitions/ultimaker2_plus.def.json
Normal file
74
resources/definitions/ultimaker2_plus.def.json
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"id": "ultimaker2_plus",
|
||||
"version": 2,
|
||||
"name": "Ultimaker 2+",
|
||||
"inherits": "ultimaker2",
|
||||
"visible": "false",
|
||||
"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": "high"
|
||||
},
|
||||
|
||||
"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]]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
69
resources/definitions/ultimaker_original.def.json
Normal file
69
resources/definitions/ultimaker_original.def.json
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"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",
|
||||
"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."
|
||||
|
|
|
|||
9
resources/instances/high_quality.inst.cfg
Normal file
9
resources/instances/high_quality.inst.cfg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[general]
|
||||
version = 2
|
||||
name = high
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
type = quality
|
||||
|
||||
[values]
|
||||
9
resources/instances/normal_quality.inst.cfg
Normal file
9
resources/instances/normal_quality.inst.cfg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[general]
|
||||
version = 2
|
||||
name = normal
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
type = quality
|
||||
|
||||
[values]
|
||||
|
|
@ -1,61 +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 },
|
||||
"retraction_enable": { "default": true, "always_visible": true },
|
||||
"retraction_speed": { "default": 50.0, "visible": false },
|
||||
"retraction_amount": { "default": 0.8, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"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,59 +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 },
|
||||
"retraction_enable": { "default": true, "always_visible": true},
|
||||
"retraction_speed": { "default": 50.0, "visible": false },
|
||||
"retraction_amount": { "default": 0.8, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"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,58 +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 },
|
||||
"retraction_speed": { "default": 30.0, "visible": false},
|
||||
"retraction_amount": { "default": 2.0, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"support_enable": { "default": true }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +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 },
|
||||
"retraction_amount": { "default": 2.0, "visible": false },
|
||||
"retraction_speed": { "default": 45.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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"id": "bq_hephestos_xl",
|
||||
"version": 1,
|
||||
"name": "BQ Prusa i3 Hephestos XL",
|
||||
"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": 200
|
||||
},
|
||||
"machine_depth": {
|
||||
"default": 300
|
||||
},
|
||||
"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 },
|
||||
"retraction_speed": { "default": 30.0, "visible": false},
|
||||
"retraction_amount": { "default": 2.0, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"support_enable": { "default": true }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"id": "bq_witbox",
|
||||
"version": 1,
|
||||
"name": "BQ Witbox",
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_witbox_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\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": 297
|
||||
},
|
||||
"machine_depth": {
|
||||
"default": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default": "RepRap"
|
||||
},
|
||||
"machine_platform_offset": {
|
||||
"default": [0, -145, -38]
|
||||
},
|
||||
"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 },
|
||||
"retraction_speed": { "default": 30.0, "visible": false},
|
||||
"retraction_amount": { "default": 2.0, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"support_enable": { "default": true }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
{
|
||||
"id": "bq_witbox_2",
|
||||
"version": 1,
|
||||
"name": "BQ Witbox 2",
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_witbox_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": 297
|
||||
},
|
||||
"machine_depth": {
|
||||
"default": 210
|
||||
},
|
||||
"machine_height": {
|
||||
"default": 200
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default": false
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default": false
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default": "RepRap"
|
||||
},
|
||||
"machine_platform_offset": {
|
||||
"default": [0, -145, -38]
|
||||
},
|
||||
"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 },
|
||||
"retraction_amount": { "default": 2.0, "visible": false },
|
||||
"retraction_speed": { "default": 45.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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"id": "dual_extrusion",
|
||||
"name": "Dual Extrusion Base File",
|
||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"visible": false,
|
||||
|
||||
"machine_extruder_trains": {
|
||||
"0": {
|
||||
"extruder_nr": { "default": 0 },
|
||||
"machine_nozzle_offset_x": { "default": 0.0 },
|
||||
"machine_nozzle_offset_y": { "default": 0.0 }
|
||||
},
|
||||
"1": {
|
||||
"extruder_nr": { "default": 1 }
|
||||
}
|
||||
},
|
||||
|
||||
"machine_settings": {
|
||||
"machine_use_extruder_offset_to_offset_coords": { "default": false },
|
||||
|
||||
"machine_nozzle_offset_x": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_nozzle_offset_y": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_start_code": { "default": "", "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_start_pos_abs": { "default": false, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_start_pos_x": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_start_pos_y": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_end_pos_abs": { "default": false, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_end_pos_x": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_end_pos_y": { "default": 0, "SEE_machine_extruder_trains": true },
|
||||
"machine_extruder_end_code": { "default": "", "SEE_machine_extruder_trains": true }
|
||||
},
|
||||
"overrides": {
|
||||
"speed_print": {
|
||||
"children": {
|
||||
"speed_prime_tower": {
|
||||
"label": "Prime Tower Speed",
|
||||
"description": "The speed at which the prime tower is printed. Printing the prime tower slower can make it more stable when the adhesion between the different filaments is suboptimal.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "150",
|
||||
"default": 60,
|
||||
"visible": false,
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"line_width": {
|
||||
"children": {
|
||||
"prime_tower_line_width": {
|
||||
"label": "Prime Tower Line Width",
|
||||
"description": "Width of a single prime tower line.",
|
||||
"unit": "mm",
|
||||
"min_value": "0.0001",
|
||||
"min_value_warning": "0.2",
|
||||
"max_value_warning": "5",
|
||||
"default": 0.4,
|
||||
"type": "float",
|
||||
"visible": false,
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"categories": {
|
||||
"dual": {
|
||||
"label": "Dual Extrusion",
|
||||
"visible": true,
|
||||
"icon": "category_dual",
|
||||
"settings": {
|
||||
"extruder_nr": {
|
||||
"label": "Extruder",
|
||||
"description": "The extruder train used for printing. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"always_visible": true
|
||||
},
|
||||
"adhesion_extruder_nr": {
|
||||
"label": "Platform Adhesion Extruder",
|
||||
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_extruder_nr": {
|
||||
"label": "Support Extruder",
|
||||
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"global_only": true,
|
||||
"children": {
|
||||
"support_infill_extruder_nr": {
|
||||
"label": "Support Infill Extruder",
|
||||
"description": "The extruder train to use for printing the infill of the support. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_extruder_nr_layer_0": {
|
||||
"label": "First Layer Support Extruder",
|
||||
"description": "The extruder train to use for printing the first layer of support infill. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_roof_extruder_nr": {
|
||||
"label": "Support Roof Extruder",
|
||||
"description": "The extruder train to use for printing the roof of the support. This is used in multi-extrusion.",
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"enabled": "support_roof_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"prime_tower_enable": {
|
||||
"label": "Enable Prime Tower",
|
||||
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
|
||||
"type": "boolean",
|
||||
"visible": true,
|
||||
"default": false,
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_size": {
|
||||
"label": "Prime Tower Size",
|
||||
"description": "The width of the prime tower.",
|
||||
"visible": false,
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 15,
|
||||
"min_value": "0",
|
||||
"max_value_warning": "20",
|
||||
"inherit_function": "15 if prime_tower_enable else 0",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_position_x": {
|
||||
"label": "Prime Tower X Position",
|
||||
"description": "The x position of the prime tower.",
|
||||
"visible": false,
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 200,
|
||||
"min_value_warning": "-1000",
|
||||
"max_value_warning": "1000",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_position_y": {
|
||||
"label": "Prime Tower Y Position",
|
||||
"description": "The y position of the prime tower.",
|
||||
"visible": false,
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 200,
|
||||
"min_value_warning": "-1000",
|
||||
"max_value_warning": "1000",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_flow": {
|
||||
"label": "Prime Tower Flow",
|
||||
"description": "Flow compensation: the amount of material extruded is multiplied by this value.",
|
||||
"visible": false,
|
||||
"unit": "%",
|
||||
"default": 100,
|
||||
"type": "float",
|
||||
"min_value": "5",
|
||||
"min_value_warning": "50",
|
||||
"max_value_warning": "150",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_wipe_enabled": {
|
||||
"label": "Wipe Nozzle on Prime tower",
|
||||
"description": "After printing the prime tower with the one nozzle, wipe the oozed material from the other nozzle off on the prime tower.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"multiple_mesh_overlap": {
|
||||
"label": "Dual Extrusion Overlap",
|
||||
"description": "Make the objects printed with different extruder trains overlap a bit. This makes the different materials bond together better.",
|
||||
"visible": false,
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 0.15,
|
||||
"min_value": "0",
|
||||
"max_value_warning": "1.0",
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_enabled": {
|
||||
"label": "Enable Ooze Shield",
|
||||
"description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_angle": {
|
||||
"label": "Ooze Shield Angle",
|
||||
"description": "The maximum angle a part in the ooze shield will have. With 0 degrees being vertical, and 90 degrees being horizontal. A smaller angle leads to less failed ooze shields, but more material.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"min_value": "0",
|
||||
"max_value": "90",
|
||||
"default": 60,
|
||||
"visible": false,
|
||||
"enabled": "ooze_shield_enabled",
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_dist": {
|
||||
"label": "Ooze Shields Distance",
|
||||
"description": "Distance of the ooze shield from the print, in the X/Y directions.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"min_value": "0",
|
||||
"max_value_warning": "30",
|
||||
"default": 2,
|
||||
"visible": false,
|
||||
"enabled": "ooze_shield_enabled",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"material": {
|
||||
"settings": {
|
||||
"material_standby_temperature": {
|
||||
"label": "Standby Temperature",
|
||||
"description": "The temperature of the nozzle when another nozzle is currently used for printing.",
|
||||
"unit": "°C",
|
||||
"type": "float",
|
||||
"default": 150,
|
||||
"min_value": "0",
|
||||
"max_value_warning": "260",
|
||||
"global_only": "True",
|
||||
"visible": false
|
||||
},
|
||||
"switch_extruder_retraction_amount": {
|
||||
"label": "Nozzle Switch Retraction Distance",
|
||||
"description": "The amount of retraction: Set at 0 for no retraction at all. This should generally be the same as the length of the heat zone.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default": 16,
|
||||
"min_value_warning": "0",
|
||||
"max_value_warning": "100",
|
||||
"visible": false,
|
||||
"inherit_function": "machine_heat_zone_length",
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"switch_extruder_retraction_speeds": {
|
||||
"label": "Nozzle Switch Retraction Speed",
|
||||
"description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"inherit": false,
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true,
|
||||
"children": {
|
||||
"switch_extruder_retraction_speed": {
|
||||
"label": "Nozzle Switch Retract Speed",
|
||||
"description": "The speed at which the filament is retracted during a nozzle switch retract. ",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"switch_extruder_prime_speed": {
|
||||
"label": "Nozzle Switch Prime Speed",
|
||||
"description": "The speed at which the filament is pushed back after a nozzle switch retraction.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"id": "grr_neo",
|
||||
"version": 1,
|
||||
"name": "German RepRap Neo",
|
||||
"manufacturer": "Other",
|
||||
"author": "Other",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "grr_neo_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
"visible": "true",
|
||||
|
||||
"overrides": {
|
||||
"machine_width": { "default": 150 },
|
||||
"machine_height": { "default": 150 },
|
||||
"machine_depth": { "default": 150 },
|
||||
"machine_center_is_zero": { "default": false },
|
||||
"machine_nozzle_size": { "default": 0.5 },
|
||||
"machine_nozzle_heat_up_speed": { "default": 2.0 },
|
||||
"machine_nozzle_cool_down_speed": { "default": 2.0 },
|
||||
"machine_head_shape_min_x": { "default": 75 },
|
||||
"machine_head_shape_min_y": { "default": 18 },
|
||||
"machine_head_shape_max_x": { "default": 18 },
|
||||
"machine_head_shape_max_y": { "default": 35 },
|
||||
"machine_nozzle_gantry_distance": { "default": 55 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default": "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": "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"
|
||||
},
|
||||
|
||||
"material_bed_temperature": { "visible": false }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"id": "innovo-inventor",
|
||||
"version": 1,
|
||||
"name": "Innovo INVENTOR",
|
||||
"manufacturer": "Other",
|
||||
"author": "AR",
|
||||
"platform": "inventor_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": {"default": 340},
|
||||
"machine_height": {"default": 290},
|
||||
"machine_depth": {"default": 300},
|
||||
"machine_heated_bed": { "default": true},
|
||||
"machine_center_is_zero": {"default": false},
|
||||
"machine_nozzle_size": {"default": 0.4},
|
||||
"machine_head_shape_min_x": {"default": 43.7},
|
||||
"machine_head_shape_min_y": {"default": 19.2},
|
||||
"machine_head_shape_max_x": {"default": 43.7},
|
||||
"machine_head_shape_max_y": {"default": 55},
|
||||
"machine_nozzle_gantry_distance": {"default": 82.3},
|
||||
"machine_nozzle_offset_x_1": {"default": 0},
|
||||
"machine_nozzle_offset_y_1": {"default": 15},
|
||||
"machine_gcode_flavor": {"default": "RepRap (Marlin/Sprinter)"},
|
||||
"machine_start_gcode": {"default": "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": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"},
|
||||
"machine_platform_offset": {"default": [-180, -0.25, 160]}
|
||||
},
|
||||
"overrides": {
|
||||
"layer_height": { "default": 0.15},
|
||||
"wall_thickness": { "default": 0.8},
|
||||
"top_bottom_thickness": { "default": 0.3, "visible": true},
|
||||
"material_print_temperature": { "default": 215, "visible": true},
|
||||
"material_bed_temperature": { "default": 60, "visible": true},
|
||||
"material_diameter": { "default": 1.75, "visible": true},
|
||||
"retraction_enable": { "default": true, "always_visible": true},
|
||||
"retraction_speed": { "default": 50.0, "visible": false },
|
||||
"retraction_amount": { "default": 2.5, "visible": false },
|
||||
"retraction_hop": { "default": 0.075, "visible": false },
|
||||
"speed_print": { "default": 60.0, "visible": true},
|
||||
"speed_infill": { "default": 100.0, "visible": true },
|
||||
"speed_topbottom": { "default": 30.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": 0.1, "default": 30.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
"id": "m180",
|
||||
"version": 1,
|
||||
"name": "Malyan M180",
|
||||
"manufacturer": "Other",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "",
|
||||
"file_formats": "application/x3g",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": { "default": 230 },
|
||||
"machine_height": { "default": 165 },
|
||||
"machine_depth": { "default": 145 },
|
||||
"machine_center_is_zero": { "default": true },
|
||||
"machine_nozzle_size": { "default": 0.4, "min_value": "0.001" },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default": [
|
||||
[ -75, 35 ],
|
||||
[ -75, -18 ],
|
||||
[ 18, -18 ],
|
||||
[ 18, 35 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": { "default": 55 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": { "default": "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": "G92 Z0\nG1 Z10 F400\nM18\nM109 S0 T0\nM104 S0 T0\nM73 P100 (end build progress)\nG162 X Y F3000\nM18" }
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"material_bed_temperature": { "visible": "True" },
|
||||
"material_diameter": {
|
||||
"default": 1.75,
|
||||
"min_value_warning": "1.5",
|
||||
"max_value_warning": "2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
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