diff --git a/.gitignore b/.gitignore index 7a67e625c9..72ba4bf565 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md index 09651e5334..b56a5ea345 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index e700b8d7be..61802167a7 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -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 diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index b53737cc80..8fb26858a5 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -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() diff --git a/cura/ConvexHullJob.py b/cura/ConvexHullJob.py index 9fb18981d3..449b233b93 100644 --- a/cura/ConvexHullJob.py +++ b/cura/ConvexHullJob.py @@ -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. diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 46c64b7590..905aeb16d4 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -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) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 8579ce7e09..eccb3e4525 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -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 diff --git a/cura/ExtruderManagerModel.py b/cura/ExtruderManagerModel.py new file mode 100644 index 0000000000..7f0baa7aa8 --- /dev/null +++ b/cura/ExtruderManagerModel.py @@ -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 \ No newline at end of file diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py new file mode 100644 index 0000000000..deef72529b --- /dev/null +++ b/cura/MachineManagerModel.py @@ -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() diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index d3eaab662c..2663cab5a0 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -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() diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py new file mode 100644 index 0000000000..7025646294 --- /dev/null +++ b/cura/PrinterOutputDevice.py @@ -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 \ No newline at end of file diff --git a/cura/ProfileReader.py b/cura/ProfileReader.py new file mode 100644 index 0000000000..36bb2c7177 --- /dev/null +++ b/cura/ProfileReader.py @@ -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.") \ No newline at end of file diff --git a/cura/SetParentOperation.py b/cura/SetParentOperation.py new file mode 100644 index 0000000000..c40ce54909 --- /dev/null +++ b/cura/SetParentOperation.py @@ -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) diff --git a/cura/SettingOverrideDecorator.py b/cura/SettingOverrideDecorator.py new file mode 100644 index 0000000000..3e8815ec67 --- /dev/null +++ b/cura/SettingOverrideDecorator.py @@ -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 \ No newline at end of file diff --git a/cura_app.py b/cura_app.py index 853edabd5e..dc748435f9 100755 --- a/cura_app.py +++ b/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) diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 610165f7a0..42b1794160 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -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": [ { diff --git a/plugins/AutoSave/AutoSave.py b/plugins/AutoSave/AutoSave.py index e621ccdc4b..2aa49e3da1 100644 --- a/plugins/AutoSave/AutoSave.py +++ b/plugins/AutoSave/AutoSave.py @@ -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 diff --git a/plugins/AutoSave/__init__.py b/plugins/AutoSave/__init__.py index 0caa02a748..7e70ebe0a2 100644 --- a/plugins/AutoSave/__init__.py +++ b/plugins/AutoSave/__init__.py @@ -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 }, } diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 8dc6a61dd8..d60154d56b 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -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. diff --git a/plugins/ChangeLogPlugin/__init__.py b/plugins/ChangeLogPlugin/__init__.py index 88ac4e08a6..8466bfaa1b 100644 --- a/plugins/ChangeLogPlugin/__init__.py +++ b/plugins/ChangeLogPlugin/__init__.py @@ -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()} \ No newline at end of file + return {"extension": ChangeLog.ChangeLog()} diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 59a802045a..9607ba407b 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -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() diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 96a477031e..59feec0fc2 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -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) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index ccd5d32eb3..cf008e7b6f 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -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") diff --git a/plugins/CuraEngineBackend/__init__.py b/plugins/CuraEngineBackend/__init__.py index 86a53d3ada..2e652ae845 100644 --- a/plugins/CuraEngineBackend/__init__.py +++ b/plugins/CuraEngineBackend/__init__.py @@ -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 } } diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index e3001da6ae..1d27649498 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -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 \ No newline at end of file diff --git a/plugins/CuraProfileWriter/CuraProfileWriter.py b/plugins/CuraProfileWriter/CuraProfileWriter.py index 1ac206e54a..82df446b8a 100644 --- a/plugins/CuraProfileWriter/CuraProfileWriter.py +++ b/plugins/CuraProfileWriter/CuraProfileWriter.py @@ -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)) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 50048d831b..11cc249657 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -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 \ No newline at end of file diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index e481e4c187..d304f0d046 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -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 diff --git a/plugins/GCodeWriter/__init__.py b/plugins/GCodeWriter/__init__.py index cd8a5d3418..efe3368c61 100644 --- a/plugins/GCodeWriter/__init__.py +++ b/plugins/GCodeWriter/__init__.py @@ -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": { diff --git a/plugins/ImageReader/__init__.py b/plugins/ImageReader/__init__.py index 69fc1ddcc3..7ebdc31e57 100644 --- a/plugins/ImageReader/__init__.py +++ b/plugins/ImageReader/__init__.py @@ -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": [ { diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 97a2a0ab30..a98fab5c8a 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -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 diff --git a/plugins/LayerView/__init__.py b/plugins/LayerView/__init__.py index 3d43532126..67750fb562 100644 --- a/plugins/LayerView/__init__.py +++ b/plugins/LayerView/__init__.py @@ -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"), diff --git a/plugins/LegacyProfileReader/DictionaryOfDoom.json b/plugins/LegacyProfileReader/DictionaryOfDoom.json index e30460f103..9dd0c04a05 100644 --- a/plugins/LegacyProfileReader/DictionaryOfDoom.json +++ b/plugins/LegacyProfileReader/DictionaryOfDoom.json @@ -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", diff --git a/plugins/LegacyProfileReader/LegacyProfileReader.py b/plugins/LegacyProfileReader/LegacyProfileReader.py index 3daf360ee6..19154c9c5a 100644 --- a/plugins/LegacyProfileReader/LegacyProfileReader.py +++ b/plugins/LegacyProfileReader/LegacyProfileReader.py @@ -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: diff --git a/plugins/LegacyProfileReader/__init__.py b/plugins/LegacyProfileReader/__init__.py index e671f02571..f8b1f5c156 100644 --- a/plugins/LegacyProfileReader/__init__.py +++ b/plugins/LegacyProfileReader/__init__.py @@ -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": [ { diff --git a/plugins/PerObjectSettingsTool/PerObjectCategory.qml b/plugins/PerObjectSettingsTool/PerObjectCategory.qml new file mode 100644 index 0000000000..2113a623a0 --- /dev/null +++ b/plugins/PerObjectSettingsTool/PerObjectCategory.qml @@ -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) +} diff --git a/plugins/PerObjectSettingsTool/PerObjectItem.qml b/plugins/PerObjectSettingsTool/PerObjectItem.qml new file mode 100644 index 0000000000..2dddf0b444 --- /dev/null +++ b/plugins/PerObjectSettingsTool/PerObjectItem.qml @@ -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() + } + } +} + + diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py new file mode 100644 index 0000000000..9ef2515bed --- /dev/null +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -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 + diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py b/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py index 7f7cef049b..74a1fbed9a 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py @@ -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) + }) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index c664aeaeef..f873ec9a79 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -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" } } } diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index dc65725839..c74800e83d 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -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": diff --git a/plugins/PerObjectSettingsTool/SettingOverrideModel.py b/plugins/PerObjectSettingsTool/SettingOverrideModel.py index 860650015c..d4bebfdfee 100644 --- a/plugins/PerObjectSettingsTool/SettingOverrideModel.py +++ b/plugins/PerObjectSettingsTool/SettingOverrideModel.py @@ -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: diff --git a/plugins/PerObjectSettingsTool/__init__.py b/plugins/PerObjectSettingsTool/__init__.py index 0d49b2c892..1779556ade 100644 --- a/plugins/PerObjectSettingsTool/__init__.py +++ b/plugins/PerObjectSettingsTool/__init__.py @@ -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() } diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py index 27859bd145..c6fc277234 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -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) diff --git a/plugins/RemovableDriveOutputDevice/__init__.py b/plugins/RemovableDriveOutputDevice/__init__.py index 635bdde008..16adcbfd7c 100644 --- a/plugins/RemovableDriveOutputDevice/__init__.py +++ b/plugins/RemovableDriveOutputDevice/__init__.py @@ -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 } } diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 8719e9c6e4..71b29c8186 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -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. diff --git a/plugins/SolidView/__init__.py b/plugins/SolidView/__init__.py index 0317648e6e..945ccba8f6 100644 --- a/plugins/SolidView/__init__.py +++ b/plugins/SolidView/__init__.py @@ -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"), diff --git a/plugins/USBPrinting/ControlWindow.qml b/plugins/USBPrinting/ControlWindow.qml index 50dfe64f1f..0938e8e6d3 100644 --- a/plugins/USBPrinting/ControlWindow.qml +++ b/plugins/USBPrinting/ControlWindow.qml @@ -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 { diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/USBPrinterOutputDevice.py similarity index 69% rename from plugins/USBPrinting/PrinterConnection.py rename to plugins/USBPrinting/USBPrinterOutputDevice.py index 7d55952984..68c4567450 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -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() diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py similarity index 65% rename from plugins/USBPrinting/USBPrinterManager.py rename to plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 6dc88f59d5..24e2148375 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -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 diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 47f5de321f..4fab439bad 100644 --- a/plugins/USBPrinting/__init__.py +++ b/plugins/USBPrinting/__init__.py @@ -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()} diff --git a/plugins/XRayView/__init__.py b/plugins/XRayView/__init__.py index 277dc69b92..34e4761863 100644 --- a/plugins/XRayView/__init__.py +++ b/plugins/XRayView/__init__.py @@ -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"), diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py new file mode 100644 index 0000000000..d73beec193 --- /dev/null +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -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" + } diff --git a/plugins/XmlMaterialProfile/__init__.py b/plugins/XmlMaterialProfile/__init__.py new file mode 100644 index 0000000000..041a3f6346 --- /dev/null +++ b/plugins/XmlMaterialProfile/__init__.py @@ -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") } + diff --git a/resources/definitions/bq_hephestos.def.json b/resources/definitions/bq_hephestos.def.json new file mode 100644 index 0000000000..65d6fc38a3 --- /dev/null +++ b/resources/definitions/bq_hephestos.def.json @@ -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 + } + } +} \ No newline at end of file diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json new file mode 100644 index 0000000000..8afd4135f1 --- /dev/null +++ b/resources/definitions/bq_hephestos_2.def.json @@ -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 } + } +} diff --git a/resources/definitions/bq_hephestos_xl.def.json b/resources/definitions/bq_hephestos_xl.def.json new file mode 100644 index 0000000000..9cf5b685db --- /dev/null +++ b/resources/definitions/bq_hephestos_xl.def.json @@ -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 + } + } +} \ No newline at end of file diff --git a/resources/definitions/bq_witbox.def.json b/resources/definitions/bq_witbox.def.json new file mode 100644 index 0000000000..77826a1576 --- /dev/null +++ b/resources/definitions/bq_witbox.def.json @@ -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 + } + + } +} \ No newline at end of file diff --git a/resources/definitions/bq_witbox_2.def.json b/resources/definitions/bq_witbox_2.def.json new file mode 100644 index 0000000000..b9d9b497cd --- /dev/null +++ b/resources/definitions/bq_witbox_2.def.json @@ -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 + } + } +} \ No newline at end of file diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json new file mode 100644 index 0000000000..f755d72c4a --- /dev/null +++ b/resources/definitions/fdmextruder.def.json @@ -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" + } + } + } + } +} diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json new file mode 100644 index 0000000000..6dca68a6c9 --- /dev/null +++ b/resources/definitions/fdmprinter.def.json @@ -0,0 +1,2810 @@ +{ + "id": "fdmprinter", + "name": "FDM Printer Base Description", + "version": 2, + "metadata": + { + "type": "machine", + "author": "Ultimaker B.V.", + "category": "Ultimaker", + "manufacturer": "Ultimaker", + "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g", + "visible": false, + "preferred_material": "pla", + "preferred_quality": "normal", + "machine_extruder_trains": + { + "0": "fdmextruder" + } + }, + "settings": + { + "machine_settings": + { + "label": "Machine", + "type": "category", + "description": "Machine specific settings", + "children": + { + "machine_show_variants": + { + "description": "Whether to show the different variants of this machine, which are described in separate json files.", + "default_value": false, + "type": "bool", + "label": "Show machine variants" + }, + "machine_start_gcode": + { + "description": "Gcode commands to be executed at the very start - separated by \\n.", + "default_value": "G28 ;Home\nG1 Z15.0 F6000 ;Move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0", + "label": "Start GCode", + "global_only": true, + "type": "str" + }, + "machine_end_gcode": + { + "description": "Gcode commands to be executed at the very end - separated by \\n.", + "default_value": "M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84", + "label": "End GCode", + "global_only": true, + "type": "str" + }, + "material_bed_temp_wait": + { + "description": "Whether to insert a command to wait until the bed temperature is reached at the start.", + "label": "Wait for bed heatup", + "default_value": true, + "global_only": true, + "type": "bool" + }, + "material_print_temp_prepend": + { + "description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.", + "default_value": true, + "global_only": true, + "type": "bool", + "label": "Wait for material heatup" + }, + "machine_width": + { + "description": "The width (X-direction) of the printable area.", + "default_value": 100, + "global_only": true, + "type": "float", + "label": "Machine width" + }, + "machine_depth": + { + "description": "The depth (Y-direction) of the printable area.", + "default_value": 100, + "global_only": true, + "type": "float", + "label": "Machine depth" + }, + "machine_height": + { + "description": "The height (Z-direction) of the printable area.", + "default_value": 100, + "global_only": true, + "type": "float", + "label": "Machine height" + }, + "machine_heated_bed": + { + "description": "Whether the machine has a heated bed present.", + "default_value": false, + "global_only": true, + "label": "Has heated bed", + "type": "bool" + }, + "machine_center_is_zero": + { + "description": "Whether the X/Y coordinates of the zero position of the printer is at the center of the printable area.", + "default_value": false, + "global_only": true, + "type": "bool", + "label": "Is center origin" + }, + "machine_extruder_count": + { + "description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.", + "default_value": 1, + "global_only": true, + "type": "int", + "label": "Number extruders" + }, + "machine_nozzle_tip_outer_diameter": + { + "description": "The outer diameter of the tip of the nozzle.", + "label": "Outer nozzle diameter", + "default_value": 1, + "global_only": true, + "type": "float" + }, + "machine_nozzle_head_distance": + { + "description": "The height difference between the tip of the nozzle and the lowest part of the print head.", + "default_value": 3, + "global_only": true, + "type": "float", + "label": "Nozzle length" + }, + "machine_nozzle_expansion_angle": + { + "description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.", + "default_value": 45, + "global_only": true, + "type": "int", + "label": "Nozzle angle" + }, + "machine_heat_zone_length": + { + "description": "The distance from the tip of the nozzle in which heat from the nozzle is transfered to the filament.", + "default_value": 16, + "global_only": true, + "type": "float", + "label": "Heat zone length" + }, + "machine_nozzle_heat_up_speed": + { + "description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.", + "default_value": 2.0, + "global_only": true, + "type": "float", + "label": "Heat up speed" + }, + "machine_nozzle_cool_down_speed": + { + "description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.", + "default_value": 2.0, + "global_only": true, + "type": "float", + "label": "Cool down speed" + }, + "machine_gcode_flavor": + { + "description": "The type of gcode to be generated.", + "default_value": "RepRap", + "global_only": true, + "type": "str", + "label": "Gcode flavour" + }, + "machine_disallowed_areas": + { + "description": "A list of polygons with areas the print head is not allowed to enter.", + "type": "polygons", + "default_value": [], + "global_only": true, + "label": "Disallowed areas" + }, + "machine_head_polygon": + { + "description": "A 2D silhouette of the print head (fan caps excluded).", + "type": "polygon", + "default_value": + [ + [ + -1, + 1 + ], + [ + -1, + -1 + ], + [ + 1, + -1 + ], + [ + 1, + 1 + ] + ], + "global_only": true, + "label": "Machine head polygon" + }, + "machine_head_with_fans_polygon": + { + "description": "A 2D silhouette of the print head (fan caps included).", + "type": "polygon", + "default_value": + [ + [ + -20, + 10 + ], + [ + 10, + 10 + ], + [ + 10, + -10 + ], + [ + -20, + -10 + ] + ], + "global_only": true, + "label": "Machine head & Fan polygon" + }, + "gantry_height": + { + "description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).", + "default_value": 99999999999, + "global_only": true, + "label": "Gantry height", + "type": "float" + }, + "machine_nozzle_size": + { + "label": "Nozzle Diameter", + "description": "The inner diameter of the nozzle. Change this setting when using a non-standard nozzle size.", + "unit": "mm", + "type": "float", + "default_value": 0.4, + "minimum_value": "0.001", + "maximum_value_warning": "10" + }, + "machine_use_extruder_offset_to_offset_coords": + { + "label": "Offset With Extruder", + "description": "Apply the extruder offset to the coordinate system.", + "type": "bool", + "default_value": true + } + } + }, + "resolution": + { + "label": "Quality", + "type": "category", + "icon": "category_layer_height", + "description": "All settings that influence the resolution of the print. These settings have a large impact on the quality (and print time)", + "children": + { + "layer_height": + { + "label": "Layer Height", + "description": "The height of each layer in mm. Higher values produce faster prints in lower resolution, lower values produce slower prints in higher resolution.", + "unit": "mm", + "type": "float", + "default_value": 0.1, + "minimum_value": "0.001", + "minimum_value_warning": "0.04", + "maximum_value_warning": "0.8 * machine_nozzle_size", + "global_only": "True" + }, + "layer_height_0": + { + "label": "Initial Layer Height", + "description": "The height of the initial layer in mm. A thicker initial layer makes adhesion to the build plate easier.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0.001", + "minimum_value_warning": "0.04", + "maximum_value_warning": "0.8 * machine_nozzle_size", + "global_only": "True" + }, + "line_width": + { + "label": "Line Width", + "description": "Width of a single line. Generally, the width of each line should correspond to the width of the nozzle. However, slightly reducing this value could produce better prints.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "2 * machine_nozzle_size", + "default_value": 0.4, + "type": "float", + "value": "machine_nozzle_size", + "children": + { + "wall_line_width": + { + "label": "Wall Line Width", + "description": "Width of a single wall line.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "value":"line_width", + "default_value": 0.4, + "type": "float", + "children": + { + "wall_line_width_0": + { + "label": "Outer Wall Line Width", + "description": "Width of the outermost wall line. By lowering this value, higher levels of detail can be printed.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "value":"wall_line_width", + "type": "float" + }, + "wall_line_width_x": + { + "label": "Inner Wall(s) Line Width", + "description": "Width of a single wall line for all wall lines except the outermost one.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "value":"wall_line_width", + "type": "float" + } + } + }, + "skin_line_width": + { + "label": "Top/bottom Line Width", + "description": "Width of a single top/bottom line.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "type": "float", + "value": "line_width" + }, + "infill_line_width": + { + "label": "Infill Line Width", + "description": "Width of a single infill line.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "type": "float", + "value": "line_width" + }, + "skirt_line_width": + { + "label": "Skirt Line Width", + "description": "Width of a single skirt line.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "type": "float", + "global_only": true, + "value": "line_width" + }, + "support_line_width": + { + "label": "Support Line Width", + "description": "Width of a single support structure line.", + "unit": "mm", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "default_value": 0.4, + "type": "float", + "enabled": "support_enable", + "global_only": true, + "value": "line_width" + }, + "support_roof_line_width": + { + "label": "Support Roof Line Width", + "description": "Width of a single support roof line.", + "unit": "mm", + "default_value": 0.4, + "minimum_value": "0.0001", + "maximum_value_warning": "machine_nozzle_size * 2", + "type": "float", + "enabled": "support_roof_enable", + "global_only": true, + "value": "line_width" + }, + "prime_tower_line_width": + { + "label": "Prime Tower Line Width", + "description": "Width of a single prime tower line.", + "type": "float", + "unit": "mm", + "enabled": "prime_tower_enable", + "default_value": 0.4, + "value": "line_width", + "minimum_value": "0.0001", + "minimum_value_warning": "0.2", + "maximum_value_warning": "5", + "global_only": "True" + } + } + } + } + }, + "shell": + { + "label": "Shell", + "icon": "category_shell", + "description": "Shell", + "type": "category", + "children": + { + "wall_thickness": + { + "label": "Wall Thickness", + "description": "The thickness of the outside walls in the horizontal direction. This value divided by the wall line width defines the number of walls.", + "unit": "mm", + "default_value": 0.8, + "minimum_value": "0", + "minimum_value_warning": "line_width", + "maximum_value_warning": "5 * line_width", + "type": "float", + "children": + { + "wall_line_count": + { + "label": "Wall Line Count", + "description": "The number of walls. When calculated by the wall thickness, this value is rounded to a whole number.", + "default_value": 2, + "minimum_value": "0", + "type": "int", + "value": "1 if magic_spiralize else max(1, round((wall_thickness - wall_line_width_0) / wall_line_width_x) + 1)" + } + } + }, + "top_bottom_thickness": + { + "label": "Top/Bottom Thickness", + "description": "The thickness of the top/bottom layers in the print. This value divided by the layer height defines the number of top/bottom layers.", + "unit": "mm", + "default_value": 0.8, + "minimum_value": "0", + "maximum_value": "5", + "minimum_value_warning": "0.6", + "type": "float", + "children": + { + "top_thickness": + { + "label": "Top Thickness", + "description": "The thickness of the top layers in the print. This value divided by the layer height defines the number of top layers.", + "unit": "mm", + "default_value": 0.8, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "float", + "value": "top_bottom_thickness", + "children": + { + "top_layers": + { + "label": "Top Layers", + "description": "The number of top layers. When calculated by the top thickness, this value is rounded to a whole number.", + "default_value": 8, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "int", + "value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / layer_height, 4))" + } + } + }, + "bottom_thickness": + { + "label": "Bottom Thickness", + "description": "The thickness of the bottom layers in the print. This value divided by the layer height defines the number of bottom layers.", + "unit": "mm", + "default_value": 0.6, + "minimum_value": "0", + "type": "float", + "value": "top_bottom_thickness", + "children": + { + "bottom_layers": + { + "label": "Bottom Layers", + "description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.", + "minimum_value": "0", + "default_value": 6, + "type": "int", + "value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / layer_height, 4))" + } + } + } + } + }, + "top_bottom_pattern": + { + "label": "Top/Bottom Pattern", + "description": "The pattern of the top/bottom layers.", + "type": "enum", + "options": + { + "lines": "Lines", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "lines" + }, + "wall_0_inset": + { + "label": "Outer Wall Inset", + "description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the object.", + "unit": "mm", + "type": "float", + "default_value": 0.0, + "value": "(machine_nozzle_size - wall_line_width_0) / 2 if wall_line_width_0 < machine_nozzle_size else 0", + "minimum_value_warning": "0", + "maximum_value_warning": "machine_nozzle_size" + }, + "alternate_extra_perimeter": + { + "label": "Alternate Extra Wall", + "description": "Prints an extra wall at every other layer. This way infill gets caught between these extra walls, resulting in stronger prints.", + "type": "bool", + "default_value": false + }, + "travel_compensate_overlapping_walls_enabled": + { + "label": "Compensate Wall Overlaps", + "description": "Compensate the flow for parts of a wall being printed where there is already a wall in place.", + "type": "bool", + "default_value": true, + "children": + { + "travel_compensate_overlapping_walls_0_enabled": { + "label": "Compensate Outer Wall Overlaps", + "description": "Compensate the flow for parts of an outer wall being printed where there is already a wall in place.", + "type": "bool", + "default_value": true, + "value": "travel_compensate_overlapping_walls_enabled" + }, + "travel_compensate_overlapping_walls_x_enabled": { + "label": "Compensate Inner Wall Overlaps", + "description": "Compensate the flow for parts of an inner wall being printed where there is already a wall in place.", + "type": "bool", + "default_value": true, + "value": "travel_compensate_overlapping_walls_enabled" + } + } + }, + "xy_offset": + { + "label": "Horizontal Expansion", + "description": "Amount of offset applied to all polygons in each layer. Positive values can compensate for too big holes; negative values can compensate for too small holes.", + "unit": "mm", + "type": "float", + "minimum_value_warning": "-10", + "maximum_value_warning": "10", + "default_value": 0 + }, + "z_seam_type": + { + "label": "Z Seam Alignment", + "description": "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these at the back, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker.", + "type": "enum", + "options": + { + "back": "Back", + "shortest": "Shortest", + "random": "Random" + }, + "default_value": "shortest" + }, + "skin_no_small_gaps_heuristic": + { + "label": "Ignore Small Z Gaps", + "description": "When the model has small vertical gaps, about 5% extra computation time can be spent on generating top and bottom skin in these narrow spaces. In such case, disable the setting.", + "type": "bool", + "default_value": true + } + } + }, + "infill": + { + "label": "Infill", + "icon": "category_infill", + "description": "Infill", + "type": "category", + "children": + { + "infill_sparse_density": + { + "label": "Infill Density", + "description": "Adjusts the density of infill of the print.", + "unit": "%", + "type": "float", + "default_value": 20, + "minimum_value": "0", + "maximum_value_warning": "100", + "children": + { + "infill_line_distance": + { + "label": "Infill Line Distance", + "description": "Distance between the printed infill lines. This setting is calculated by the infill density and the infill line width.", + "unit": "mm", + "type": "float", + "default_value": 2, + "minimum_value": "0", + "value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else 1))" + } + } + }, + "infill_pattern": + { + "label": "Infill Pattern", + "description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle and concentric patterns are fully printed every layer.", + "type": "enum", + "options": + { + "grid": "Grid", + "lines": "Lines", + "triangles": "Triangles", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "grid", + "value": "'lines' if infill_sparse_density > 25 else 'grid'" + }, + "infill_overlap": + { + "label": "Infill Overlap Percentage", + "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", + "unit": "%", + "type": "float", + "default_value": 10, + "value": "10 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0", + "minimum_value_warning": "-50", + "maximum_value_warning": "100", + "enabled": "infill_pattern != 'concentric'", + "children": + { + "infill_overlap_mm": + { + "label": "Infill Overlap", + "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", + "unit": "mm", + "type": "float", + "default_value": 0.04, + "minimum_value_warning": "-0.5 * machine_nozzle_size", + "maximum_value_warning": "machine_nozzle_size", + "value": "infill_line_width * infill_overlap / 100 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0", + "enabled": "infill_pattern != 'concentric'" + } + } + }, + "skin_overlap": { + "label": "Skin Overlap Percentage", + "description": "The amount of overlap between the skin and the walls. A slight overlap allows the walls to connect firmly to the skin.", + "unit": "%", + "type": "float", + "default_value": 5, + "minimum_value_warning": "-50", + "maximum_value_warning": "100", + "value": "5 if top_bottom_pattern != 'concentric' else 0", + "enabled": "top_bottom_pattern != 'concentric'", + "children": { + "skin_overlap_mm": { + "label": "Skin Overlap", + "description": "The amount of overlap between the skin and the walls. A slight overlap allows the walls to connect firmly to the skin.", + "unit": "mm", + "type": "float", + "default_value": 0.02, + "minimum_value_warning": "-0.5 * machine_nozzle_size", + "maximum_value_warning": "machine_nozzle_size", + "value": "skin_line_width * skin_overlap / 100 if top_bottom_pattern != 'concentric' else 0", + "enabled": "top_bottom_pattern != 'concentric'" + } + } + }, + "infill_wipe_dist": + { + "label": "Infill Wipe Distance", + "description": "Distance of a travel move inserted after every infill line, to make the infill stick to the walls better. This option is similar to infill overlap, but without extrusion and only on one end of the infill line.", + "unit": "mm", + "type": "float", + "default_value": 0.04, + "value": "wall_line_width_0 / 4 if wall_line_count == 1 else wall_line_width_x / 4", + "minimum_value_warning": "0", + "maximum_value_warning": "machine_nozzle_size" + }, + "infill_sparse_thickness": + { + "label": "Infill Layer Thickness", + "description": "The thickness per layer of infill material. This value should always be a multiple of the layer height and is otherwise rounded.", + "unit": "mm", + "type": "float", + "default_value": 0.1, + "minimum_value": "0.0001", + "maximum_value_warning": "0.32", + "maximum_value": "layer_height * 8", + "value": "layer_height" + }, + "infill_before_walls": + { + "label": "Infill Before Walls", + "description": "Print the infill before printing the walls. Printing the walls first may lead to more accurate walls, but overhangs print worse. Printing the infill first leads to sturdier walls, but the infill pattern might sometimes show through the surface.", + "type": "bool", + "default_value": true + } + } + }, + "material": + { + "label": "Material", + "icon": "category_material", + "description": "Material", + "type": "category", + "children": + { + "material_flow_dependent_temperature": + { + "label": "Auto Temperature", + "description": "Change the temperature for each layer automatically with the average flow speed of that layer.", + "type": "bool", + "default_value": false, + "enabled": "False", + "global_only": true + }, + "material_print_temperature": + { + "label": "Printing Temperature", + "description": "The temperature used for printing. Set at 0 to pre-heat the printer manually.", + "unit": "°C", + "type": "float", + "default_value": 210, + "minimum_value": "0", + "maximum_value_warning": "260", + "enabled": "not (material_flow_dependent_temperature)" + }, + "material_flow_temp_graph": + { + "label": "Flow Temperature Graph", + "description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).", + "unit": "", + "type": "str", + "default_value": "[[3.5,200],[7.0,240]]", + "enabled": "False", + "comments": "old enabled function: material_flow_dependent_temperature", + "global_only": true + }, + "material_extrusion_cool_down_speed": { + "label": "Extrusion Cool Down Speed Modifier", + "description": "The extra speed by which the nozzle cools while extruding. The same value is used to signify the heat up speed lost when heating up while extruding.", + "unit": "°C/s", + "type": "float", + "default_value": 0.5, + "minimum_value": "0", + "maximum_value_warning": "10.0", + "global_only": "True", + "enabled": "False", + "comments": "old enabled function: material_flow_dependent_temperature or machine_extruder_count > 1" + }, + "material_bed_temperature": { + "label": "Bed Temperature", + "description": "The temperature used for the heated bed. Set at 0 to pre-heat the printer manually.", + "unit": "°C", + "type": "float", + "default_value": 60, + "minimum_value": "0", + "maximum_value_warning": "260", + "enabled": "machine_heated_bed", + "global_only": "True" + }, + "material_diameter": { + "label": "Diameter", + "description": "Adjusts the diameter of the filament used. Match this value with the diameter of the used filament.", + "unit": "mm", + "type": "float", + "default_value": 2.85, + "minimum_value": "0.0001", + "minimum_value_warning": "0.4", + "maximum_value_warning": "3.5", + "global_only": "True" + }, + "material_flow": { + "label": "Flow", + "description": "Flow compensation: the amount of material extruded is multiplied by this value.", + "unit": "%", + "default_value": 100, + "type": "float", + "minimum_value": "5", + "minimum_value_warning": "50", + "maximum_value_warning": "150" + }, + "retraction_enable": { + "label": "Enable Retraction", + "description": "Retract the filament when the nozzle is moving over a non-printed area. ", + "type": "bool", + "default_value": true + }, + "retraction_amount": { + "label": "Retraction Distance", + "description": "The length of material retracted during a retraction move.", + "unit": "mm", + "type": "float", + "default_value": 6.5, + "minimum_value_warning": "-0.0001", + "maximum_value_warning": "10.0", + "enabled": "retraction_enable" + }, + "retraction_speed": { + "label": "Retraction Speed", + "description": "The speed at which the filament is retracted and primed during a retraction move.", + "unit": "mm/s", + "type": "float", + "default_value": 25, + "minimum_value": "0", + "maximum_value": "299792458000", + "maximum_value_warning": "100", + "enabled": "retraction_enable", + "children": { + "retraction_retract_speed": { + "label": "Retraction Retract Speed", + "description": "The speed at which the filament is retracted during a retraction move.", + "unit": "mm/s", + "type": "float", + "default_value": 25, + "minimum_value": "0", + "maximum_value": "299792458000", + "maximum_value_warning": "100", + "enabled": "retraction_enable", + "value": "retraction_speed" + }, + "retraction_prime_speed": { + "label": "Retraction Prime Speed", + "description": "The speed at which the filament is primed during a retraction move.", + "unit": "mm/s", + "type": "float", + "default_value": 25, + "minimum_value": "0", + "maximum_value": "299792458000", + "maximum_value_warning": "100", + "enabled": "retraction_enable", + "value": "retraction_speed" + } + } + }, + "retraction_extra_prime_amount": { + "label": "Retraction Extra Prime Amount", + "description": "Some material can ooze away during a travel move, which can be compensated for here.", + "unit": "mm³", + "type": "float", + "default_value": 0, + "minimum_value_warning": "-0.0001", + "maximum_value_warning": "5.0", + "enabled": "retraction_enable" + }, + "retraction_min_travel": { + "label": "Retraction Minimum Travel", + "description": "The minimum distance of travel needed for a retraction to happen at all. This helps to get fewer retractions in a small area.", + "unit": "mm", + "type": "float", + "default_value": 1.5, + "value": "line_width * 2", + "minimum_value": "0", + "maximum_value_warning": "10", + "enabled": "retraction_enable" + }, + "retraction_count_max": { + "label": "Maximum Retraction Count", + "description": "This setting limits the number of retractions occurring within the minimum extrusion distance window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament, as that can flatten the filament and cause grinding issues.", + "default_value": 45, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "int", + "enabled": "retraction_enable" + }, + "retraction_extrusion_window": { + "label": "Minimum Extrusion Distance Window", + "description": "The window in which the maximum retraction count is enforced. This value should be approximately the same as the retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", + "unit": "mm", + "type": "float", + "default_value": 4.5, + "minimum_value": "0", + "maximum_value_warning": "retraction_amount * 2", + "value": "retraction_amount", + "enabled": "retraction_enable" + }, + "retraction_hop": { + "label": "Z Hop when Retracting", + "description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.", + "unit": "mm", + "type": "float", + "default_value": 0, + "minimum_value_warning": "-0.0001", + "maximum_value_warning": "10", + "enabled": "retraction_enable" + }, + "material_standby_temperature": + { + "label": "Standby Temperature", + "description": "The temperature of the nozzle when another nozzle is currently used for printing.", + "type": "float", + "unit": "°C", + "default_value": 150, + "minimum_value": "0", + "maximum_value_warning": "260", + "global_only": "True" + }, + "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.", + "type": "float", + "unit": "mm", + "enabled": "retraction_enable", + "default_value": 20, + "value": "machine_heat_zone_length", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "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.", + "type": "float", + "unit": "mm/s", + "enabled": "retraction_enable", + "default_value": 20, + "minimum_value": "0.1", + "maximum_value_warning": "300", + "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.", + "type": "float", + "unit": "mm/s", + "enabled": "retraction_enable", + "default_value": 20, + "value": "switch_extruder_retraction_speeds", + "minimum_value": "0.1", + "maximum_value_warning": "300", + "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.", + "type": "float", + "unit": "mm/s", + "enabled": "retraction_enable", + "default_value": 20, + "value": "switch_extruder_retraction_speeds", + "minimum_value": "0.1", + "maximum_value_warning": "300", + "global_only": "True" + } + } + }, + "switch_extruder_retraction_hop": + { + "label": "Nozzle Switch Z Hop", + "description": "Whenever the machine switches to another nozzle, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle which has been unused for a while from oozing material on the outside of the print.", + "type": "float", + "unit": "mm", + "default_value": 1, + "minimum_value_warning": "-0.0001", + "maximum_value_warning": "10", + "enabled": "retraction_enable" + } + } + }, + "speed": + { + "label": "Speed", + "icon": "category_speed", + "description": "Speed", + "type": "category", + "children": + { + "speed_print": + { + "label": "Print Speed", + "description": "The speed at which printing happens.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value_warning": "150", + "maximum_value": "299792458000", + "default_value": 60, + "children": + { + "speed_infill": + { + "label": "Infill Speed", + "description": "The speed at which infill is printed.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 60, + "value": "speed_print" + }, + "speed_wall": + { + "label": "Wall Speed", + "description": "The speed at which the walls are printed.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 30, + "value": "speed_print / 2", + "children": + { + "speed_wall_0": + { + "label": "Outer Wall Speed", + "description": "The speed at which the outermost walls are printed. Printing the outer wall at a lower speed improves the final skin quality. However, having a large difference between the inner wall speed and the outer wall speed will effect quality in a negative way.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 30, + "value": "speed_wall" + }, + "speed_wall_x": + { + "label": "Inner Wall Speed", + "description": "The speed at which all inner walls are printed Printing the inner wall faster than the outer wall will reduce printing time. It works well to set this in between the outer wall speed and the infill speed.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 60, + "value": "speed_wall * 2" + } + } + }, + "speed_topbottom": + { + "label": "Top/Bottom Speed", + "description": "The speed at which top/bottom layers are printed.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 30, + "value": "speed_print / 2" + }, + "speed_support": + { + "label": "Support Speed", + "description": "The speed at which the support structure is printed. Printing support at higher speeds can greatly reduce printing time. The surface quality of the support structure is not important since it is removed after printing.", + "unit": "mm/s", + "type": "float", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "default_value": 60, + "value": "speed_print", + "enabled": "support_roof_enable", + "children": + { + "speed_support_infill": + { + "label": "Support Infill Speed", + "description": "The speed at which the infill of support is printed. Printing the infill at lower speeds improves stability.", + "unit": "mm/s", + "type": "float", + "default_value": 60, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "value": "speed_support", + "enabled": "support_enable", + "global_only": true + }, + "speed_support_roof": + { + "label": "Support Roof Speed", + "description": "The speed at which the roofs of support are printed. Printing the support roof at lower speeds can improve overhang quality.", + "unit": "mm/s", + "type": "float", + "default_value": 40, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "enabled": "support_roof_enable", + "value": "speed_support / 1.5", + "global_only": true + } + } + }, + "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.", + "type": "float", + "unit": "mm/s", + "enabled": "prime_tower_enable", + "default_value": 60, + "value": "speed_print", + "minimum_value": "0.1", + "maximum_value_warning": "150", + "global_only": "True" + } + } + }, + "speed_travel": + { + "label": "Travel Speed", + "description": "The speed at which travel moves are made.", + "unit": "mm/s", + "type": "float", + "default_value": 120, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "300", + "value": "speed_print if magic_spiralize else 120", + "global_only": true + }, + "speed_layer_0": { + "label": "Initial Layer Speed", + "description": "The print speed for the initial layer. A lower value is advised to improve adhesion to the build plate.", + "unit": "mm/s", + "type": "float", + "default_value": 30, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "300" + }, + "skirt_speed": { + "label": "Skirt Speed", + "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt at a different speed.", + "unit": "mm/s", + "type": "float", + "default_value": 30, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "300", + "value": "speed_layer_0", + "global_only": true + }, + "speed_slowdown_layers": + { + "label": "Number of Slower Layers", + "description": "The first few layers are printed slower than the rest of the object, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.", + "type": "int", + "default_value": 2, + "minimum_value": "0", + "maximum_value": "299792458000", + "maximum_value_warning": "300", + "global_only": true + } + } + }, + "travel": + { + "label": "Travel", + "icon": "category_travel", + "description": "travel", + "type": "category", + "children": + { + "retraction_combing": + { + "label": "Combing Mode", + "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.", + "type": "enum", + "options": + { + "off": "Off", + "all": "All", + "noskin": "No Skin" + }, + "default_value": "all", + "global_only": true + }, + "travel_avoid_other_parts": + { + "label": "Avoid Printed Parts when Traveling", + "description": "The nozzle avoids already printed parts when traveling. This option is only available when combing is enabled.", + "type": "bool", + "default_value": true, + "enabled": "retraction_combing != \"off\"", + "global_only": "True" + }, + "travel_avoid_distance": + { + "label": "Travel Avoid Distance", + "description": "The distance between the nozzle and already printed parts when avoiding during travel moves.", + "unit": "mm", + "type": "float", + "default_value": 1.5, + "value": "machine_nozzle_tip_outer_diameter / 2 * 1.25", + "minimum_value": "0", + "maximum_value_warning": "machine_nozzle_tip_outer_diameter * 5", + "enabled": "retraction_combing != \"off\" and travel_avoid_other_parts", + "global_only": "True" + } + } + }, + "cooling": + { + "label": "Cooling", + "icon": "category_cool", + "description": "Cooling", + "type": "category", + "children": + { + "cool_fan_enabled": + { + "label": "Enable Cooling Fans", + "description": "Enables the cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.", + "type": "bool", + "default_value": true, + "global_only": "True" + }, + "cool_fan_speed": + { + "label": "Fan Speed", + "description": "The speed at which the cooling fans spin.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 100, + "value": "100.0 if cool_fan_enabled else 0.0", + "enabled": "cool_fan_enabled", + "global_only": "True", + "children": + { + "cool_fan_speed_min": + { + "label": "Regular Fan Speed", + "description": "The speed at which the fans spin before hitting the threshold. When a layer prints faster than the threshold, the fan speed gradually inclines towards the maximum fan speed.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "value": "cool_fan_speed", + "default_value": 100, + "enabled": "cool_fan_enabled", + "global_only": "True" + }, + "cool_fan_speed_max": + { + "label": "Maximum Fan Speed", + "description": "The speed at which the fans spin on the minimum layer time. The fan speed gradually increases between the regular fan speed and maximum fan speed when the threshold is hit.", + "unit": "%", + "type": "float", + "minimum_value": "max(0, cool_fan_speed_min)", + "maximum_value": "100", + "default_value": 100, + "enabled": "cool_fan_enabled", + "global_only": "True", + "value": "cool_fan_speed" + } + } + }, + "cool_min_layer_time_fan_speed_max": + { + "label": "Regular/Maximum Fan Speed Threshold", + "description": "The layer time which sets the threshold between regular fan speed and maximum fan speed. Layers that print slower than this time use regular fan speed. For faster layers the fan speed gradually increases towards the maximum fan speed.", + "unit": "sec", + "type": "float", + "default_value": 10, + "minimum_value": "cool_min_layer_time", + "maximum_value_warning": "600", + "global_only": "True" + }, + "cool_fan_full_at_height": + { + "label": "Regular Fan Speed at Height", + "description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from zero to regular fan speed.", + "unit": "mm", + "type": "float", + "default_value": 0.5, + "value": "layer_height_0", + "minimum_value": "0", + "maximum_value_warning": "10.0", + "global_only": "True", + "children": + { + "cool_fan_full_layer": + { + "label": "Regular Fan Speed at Layer", + "description": "The layer at which the fans spin on regular fan speed. If regular fan speed at height is set, this value is calculated and rounded to a whole number.", + "type": "int", + "default_value": 1, + "minimum_value": "0", + "maximum_value_warning": "100", + "value": "max(0, int(round((cool_fan_full_at_height - layer_height_0) / layer_height, 0)))", + "global_only": "True" + } + } + }, + "cool_min_layer_time": + { + "label": "Minimum Layer Time", + "description": "The minimum time spent in a layer. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer.", + "unit": "sec", + "type": "float", + "default_value": 5, + "minimum_value": "0", + "maximum_value_warning": "600", + "global_only": "True" + }, + "cool_min_speed": + { + "label": "Minimum Speed", + "description": "The minimum print speed, despite slowing down due to the minimum layer time. When the printer would slow down too much, the pressure in the nozzle would be too low and result in bad print quality.", + "unit": "mm/s", + "type": "float", + "default_value": 10, + "minimum_value": "0", + "maximum_value_warning": "100", + "global_only": "True" + }, + "cool_lift_head": + { + "label": "Lift Head", + "description": "When the minimum speed is hit because of minimum layer time, lift the head away from the print and wait the extra time until the minimum layer time is reached.", + "type": "bool", + "default_value": false, + "global_only": "True" + } + } + }, + "support": + { + "label": "Support", + "type": "category", + "icon": "category_support", + "description": "Support", + "children": + { + "support_enable": + { + "label": "Enable Support", + "description": "Enable support structures. These structures support parts of the model with severe overhangs.", + "type": "bool", + "default_value": false + }, + "support_type": + { + "label": "Support Placement", + "description": "Adjusts the placement of the support structures. The placement can be set to touching build plate or everywhere. When set to everywhere the support structures will also be printed on the model.", + "type": "enum", + "options": + { + "buildplate": "Touching Buildplate", + "everywhere": "Everywhere" + }, + "default_value": "everywhere", + "enabled": "support_enable" + }, + "support_angle": + { + "label": "Support Overhang Angle", + "description": "The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "maximum_value": "90", + "default_value": 50, + "enabled": "support_enable" + }, + "support_pattern": + { + "label": "Support Pattern", + "description": "The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.", + "type": "enum", + "options": + { + "lines": "Lines", + "grid": "Grid", + "triangles": "Triangles", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "zigzag", + "enabled": "support_enable", + "global_only": true + }, + "support_connect_zigzags": + { + "label": "Connect Support ZigZags", + "description": "Connect the ZigZags. This will increase the strength of the zig zag support structure.", + "type": "bool", + "default_value": true, + "enabled": "support_enable and (support_pattern == \"zigzag\")", + "global_only": true + }, + "support_infill_rate": + { + "label": "Support Density", + "description": "Adjusts the density of the support structure. A higher value results in better overhangs, but the supports are harder to remove.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "100", + "default_value": 15, + "enabled": "support_enable", + "global_only": true, + "children": { + "support_line_distance": + { + "label": "Support Line Distance", + "description": "Distance between the printed support structure lines. This setting is calculated by the support density.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "default_value": 2.66, + "enabled": "support_enable", + "value": "(support_line_width * 100) / support_infill_rate * (2 if support_pattern == \"grid\" else (3 if support_pattern == \"triangles\" else 1))", + "global_only": true + } + } + }, + "support_z_distance": + { + "label": "Support Z Distance", + "description": "Distance from the top/bottom of the support structure to the print. This gap provides clearance to remove the supports after the model is printed. This value is rounded down to a multiple of the layer height.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "10", + "default_value": 0.15, + "enabled": "support_enable", + + "children": + { + "support_top_distance": + { + "label": "Support Top Distance", + "description": "Distance from the top of the support to the print.", + "unit": "mm", + "minimum_value": "0", + "maximum_value_warning": "10", + "default_value": 0.15, + "type": "float", + "enabled": "support_enable", + "value": "support_z_distance" + }, + "support_bottom_distance": + { + "label": "Support Bottom Distance", + "description": "Distance from the print to the bottom of the support.", + "unit": "mm", + "minimum_value": "0", + "maximum_value_warning": "10", + "default_value": 0.1, + "value": "0.1 if support_type == 'everywhere' else 0", + "type": "float", + "enabled": "support_enable and support_type == 'everywhere'" + } + } + }, + "support_xy_distance": + { + "label": "Support X/Y Distance", + "description": "Distance of the support structure from the print in the X/Y directions.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "10", + "default_value": 0.7, + "enabled": "support_enable" + }, + "support_xy_overrides_z": { + "label": "Support Distance Priority", + "description": "Whether the Support X/Y Distance overrides the Support Z Distance or vice versa. When X/Y overrides Z the X/Y distance can push away the support from the model, influencing the actual Z distance to the overhang. We can disable this by not applying the X/Y distance around overhangs.", + "type": "enum", + "options": { + "xy_overrides_z": "X/Y overrides Z", + "z_overrides_xy": "Z overrides X/Y" + }, + "default_value": "z_overrides_xy", + "enabled": "support_enable" + }, + "support_xy_distance_overhang": { + "label": "Minimum Support X/Y Distance", + "description": "Distance of the support structure from the overhang in the X/Y directions. ", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "10", + "default_value": 0.2, + "value": "machine_nozzle_size / 2", + "enabled": "support_enable and support_xy_overrides_z=='z_overrides_xy'" + }, + "support_bottom_stair_step_height": + { + "label": "Support Stair Step Height", + "description": "The height of the steps of the stair-like bottom of support resting on the model. A low value makes the support harder to remove, but too high values can lead to unstable support structures.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0", + "maximum_value_warning": "1.0", + "enabled": "support_enable" + }, + "support_join_distance": + { + "label": "Support Join Distance", + "description": "The maximum distance between support structures in the X/Y directions. When seperate structures are closer together than this value, the structures merge into one.", + "unit": "mm", + "type": "float", + "default_value": 2.0, + "minimum_value_warning": "0", + "maximum_value_warning": "10", + "enabled": "support_enable" + }, + "support_offset": + { + "label": "Support Horizontal Expansion", + "description": "Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.", + "unit": "mm", + "type": "float", + "default_value": 0.2, + "minimum_value_warning": "-0.5", + "maximum_value_warning": "5.0", + "enabled": "support_enable" + }, + "support_area_smoothing": + { + "label": "Support Area Smoothing", + "description": "Maximum distance in the X/Y directions of a line segment which is to be smoothed out. Ragged lines are introduced by the join distance and support bridge, which cause the machine to resonate. Smoothing the support areas won't cause them to break with the constraints, except it might change the overhang.", + "unit": "mm", + "type": "float", + "default_value": 0.6, + "minimum_value": "0", + "maximum_value_warning": "1.0", + "enabled": "support_enable" + }, + "support_roof_enable": + { + "label": "Enable Support Roof", + "description": "Generate a dense top skin at the top of the support on which the model is printed.", + "type": "bool", + "default_value": false, + "enabled": "support_enable" + }, + "support_roof_height": + { + "label": "Support Roof Thickness", + "description": "The thickness of the support roofs.", + "unit": "mm", + "type": "float", + "default_value": 1, + "minimum_value": "0", + "maximum_value_warning": "10", + "enabled": "support_roof_enable" + }, + "support_roof_density": + { + "label": "Support Roof Density", + "description": "Adjusts the density of the roof of the support structure. A higher value results in better overhangs, but the supports are harder to remove.", + "unit": "%", + "type": "float", + "default_value": 100, + "minimum_value": "0", + "maximum_value_warning": "100", + "enabled":"support_roof_enable", + "global_only": true, + "children": + { + "support_roof_line_distance": + { + "label": "Support Roof Line Distance", + "description": "Distance between the printed support roof lines. This setting is calculated by the support roof Density, but can be adjusted separately.", + "unit": "mm", + "type": "float", + "default_value": 0.4, + "minimum_value": "0", + "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))", + "enabled": "support_roof_enable", + "global_only": true + } + } + }, + "support_roof_pattern": + { + "label": "Support Roof Pattern", + "description": "The pattern with which the top of the support is printed.", + "type": "enum", + "options": + { + "lines": "Lines", + "grid": "Grid", + "triangles": "Triangles", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "concentric", + "enabled": "support_roof_enable", + "global_only": true + }, + "support_use_towers": + { + "label": "Use Towers", + "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", + "type": "bool", + "default_value": true, + "enabled": "support_enable" + }, + "support_tower_diameter": + { + "label": "Tower Diameter", + "description": "The diameter of a special tower.", + "unit": "mm", + "type": "float", + "default_value": 3.0, + "minimum_value": "0", + "maximum_value_warning": "10", + "enabled": "support_enable and support_use_towers" + }, + "support_minimal_diameter": + { + "label": "Minimum Diameter", + "description": "Minimum diameter in the X/Y directions of a small area which is to be supported by a specialized support tower.", + "unit": "mm", + "type": "float", + "default_value": 3.0, + "minimum_value": "0", + "maximum_value_warning": "10", + "maximum_value": "support_tower_diameter", + "enabled": "support_enable and support_use_towers" + }, + "support_tower_roof_angle": + { + "label": "Tower Roof Angle", + "description": "The angle of a rooftop of a tower. A higher value results in pointed tower roofs, a lower value results in flattened tower roofs.", + "unit": "°", + "type": "int", + "minimum_value": "0", + "maximum_value": "90", + "default_value": 65, + "enabled": "support_enable and support_use_towers" + } + } + }, + "platform_adhesion": + { + "label": "Platform Adhesion", + "type": "category", + "icon": "category_adhesion", + "description": "Adhesion", + "children": + { + "adhesion_type": + { + "label": "Platform Adhesion Type", + "description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your object to prevent warping. Raft adds a thick grid with a roof below the object. Skirt is a line printed around the object, but not connected to the model.", + "type": "enum", + "options": + { + "skirt": "Skirt", + "brim": "Brim", + "raft": "Raft" + }, + "default_value": "brim", + "global_only": "True" + }, + "skirt_line_count": + { + "label": "Skirt Line Count", + "description": "Multiple skirt lines help to prime your extrusion better for small objects. Setting this to 0 will disable the skirt.", + "type": "int", + "default_value": 1, + "minimum_value": "0", + "maximum_value_warning": "10", + "enabled": "adhesion_type == \"skirt\"", + "global_only": "True" + }, + "skirt_gap": + { + "label": "Skirt Distance", + "description": "The horizontal distance between the skirt and the first layer of the print.\nThis is the minimum distance, multiple skirt lines will extend outwards from this distance.", + "unit": "mm", + "type": "float", + "default_value": 3, + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "adhesion_type == \"skirt\"", + "global_only": "True" + }, + "skirt_minimal_length": + { + "label": "Skirt Minimum Length", + "description": "The minimum length of the skirt. If this length is not reached by the skirt line count, more skirt lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", + "unit": "mm", + "type": "float", + "default_value": 250, + "minimum_value": "0", + "minimum_value_warning": "25", + "maximum_value_warning": "2500", + "enabled": "adhesion_type == \"skirt\"", + "global_only": "True" + }, + "brim_width": + { + "label": "Brim Width", + "description": "The distance from the model to the outermost brim line. A larger brim enhances adhesion to the build plate, but also reduces the effective print area.", + "type": "float", + "unit": "mm", + "default_value": 8.0, + "minimum_value": "0.0", + "maximum_value_warning": "100.0", + "enabled": "adhesion_type == \"brim\"", + "global_only": "True", + "children": + { + "brim_line_count": + { + "label": "Brim Line Count", + "description": "The number of lines used for a brim. More brim lines enhance adhesion to the build plate, but also reduces the effective print area.", + "type": "int", + "default_value": 20, + "minimum_value": "0", + "maximum_value_warning": "300", + "value": "math.ceil(brim_width / skirt_line_width)", + "enabled": "adhesion_type == \"brim\"", + "global_only": "True" + } + } + }, + "raft_margin": + { + "label": "Raft Extra Margin", + "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", + "unit": "mm", + "type": "float", + "default_value": 5, + "minimum_value_warning": "0", + "maximum_value_warning": "10", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_airgap": + { + "label": "Raft Air Gap", + "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0", + "maximum_value_warning": "1.0", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "layer_0_z_overlap": { + "label": "Initial Layer Z Overlap", + "description": "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.", + "unit": "mm", + "type": "float", + "default_value": 0.22, + "value": "raft_airgap / 2", + "minimum_value": "0", + "maximum_value_warning": "layer_height", + "enabled": "adhesion_type == 'raft'", + "global_only": "True" + }, + "raft_surface_layers": + { + "label": "Raft Top Layers", + "description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers result in a smoother top surface than 1.", + "type": "int", + "default_value": 2, + "minimum_value": "0", + "maximum_value_warning": "20", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_surface_thickness": + { + "label": "Raft Top Layer Thickness", + "description": "Layer thickness of the top raft layers.", + "unit": "mm", + "type": "float", + "default_value": 0.1, + "minimum_value": "0", + "maximum_value_warning": "2.0", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_surface_line_width": + { + "label": "Raft Top Line Width", + "description": "Width of the lines in the top surface of the raft. These can be thin lines so that the top of the raft becomes smooth.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0.0001", + "maximum_value_warning": "machine_nozzle_size * 2", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_surface_line_spacing": + { + "label": "Raft Top Spacing", + "description": "The distance between the raft lines for the top raft layers. The spacing should be equal to the line width, so that the surface is solid.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0.0001", + "maximum_value_warning": "5.0", + "enabled": "adhesion_type == \"raft\"", + "value": "raft_surface_line_width", + "global_only": "True" + }, + "raft_interface_thickness": + { + "label": "Raft Middle Thickness", + "description": "Layer thickness of the middle raft layer.", + "unit": "mm", + "type": "float", + "default_value": 0.27, + "minimum_value": "0", + "maximum_value_warning": "5.0", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_interface_line_width": + { + "label": "Raft Middle Line Width", + "description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.", + "unit": "mm", + "type": "float", + "default_value": 1, + "value": "line_width", + "minimum_value": "0.0001", + "maximum_value_warning": "machine_nozzle_size * 2", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_interface_line_spacing": + { + "label": "Raft Middle Spacing", + "description": "The distance between the raft lines for the middle raft layer. The spacing of the middle should be quite wide, while being dense enough to support the top raft layers.", + "unit": "mm", + "type": "float", + "default_value": 1.0, + "minimum_value": "0", + "maximum_value_warning": "15.0", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_base_thickness": + { + "label": "Raft Base Thickness", + "description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer bed.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0", + "maximum_value_warning": "5.0", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_base_line_width": + { + "label": "Raft Base Line Width", + "description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.", + "unit": "mm", + "type": "float", + "default_value": 1, + "minimum_value": "0.0001", + "value": "line_width", + "maximum_value_warning": "machine_nozzle_size * 2", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_base_line_spacing": + { + "label": "Raft Line Spacing", + "description": "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate.", + "unit": "mm", + "type": "float", + "default_value": 3.0, + "minimum_value": "0.0001", + "maximum_value_warning": "100", + "enabled": "adhesion_type == \"raft\"", + "global_only": "True" + }, + "raft_speed": + { + "label": "Raft Print Speed", + "description": "The speed at which the raft is printed.", + "unit": "mm/s", + "type": "float", + "default_value": 30, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "200", + "enabled": "adhesion_type == \"raft\"", + "value": "speed_print / 60 * 30", + "global_only": "True", + "children": + { + "raft_surface_speed": + { + "label": "Raft Surface Print Speed", + "description": "The speed at which the surface raft layers are printed. These should be printed a bit slower, so that the nozzle can slowly smooth out adjacent surface lines.", + "unit": "mm/s", + "type": "float", + "default_value": 30, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "100", + "enabled": "adhesion_type == \"raft\"", + "value": "raft_speed", + "global_only": "True" + }, + "raft_interface_speed": + { + "label": "Raft Interface Print Speed", + "description": "The speed at which the interface raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.", + "unit": "mm/s", + "type": "float", + "default_value": 15, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "150", + "enabled": "adhesion_type == \"raft\"", + "value": "0.5 * raft_speed", + "global_only": "True" + }, + "raft_base_speed": + { + "label": "Raft Base Print Speed", + "description": "The speed at which the base raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.", + "unit": "mm/s", + "type": "float", + "default_value": 15, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "200", + "enabled": "adhesion_type == \"raft\"", + "value": "0.5 * raft_speed", + "global_only": "True" + } + } + }, + "raft_fan_speed": + { + "label": "Raft Fan Speed", + "description": "The fan speed for the raft.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "global_only": "True", + "enabled": "adhesion_type == \"raft\"", + "children": + { + "raft_surface_fan_speed": + { + "label": "Raft Surface Fan Speed", + "description": "The fan speed for the surface raft layers.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "global_only": "True", + "value": "raft_fan_speed", + "enabled": "adhesion_type == \"raft\"" + }, + "raft_interface_fan_speed": + { + "label": "Raft Interface Fan Speed", + "description": "The fan speed for the interface raft layer.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "global_only": "True", + "value": "raft_fan_speed", + "enabled": "adhesion_type == \"raft\"" + }, + "raft_base_fan_speed": + { + "label": "Raft Base Fan Speed", + "description": "The fan speed for the base raft layer.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "global_only": "True", + "value": "raft_fan_speed", + "enabled": "adhesion_type == \"raft\"" + } + } + } + } + }, + "meshfix": + { + "label": "Mesh Fixes", + "type": "category", + "icon": "category_fixes", + "description": "category_fixes", + "children": + { + "meshfix_union_all": + { + "label": "Union Overlapping Volumes", + "description": "Ignore the internal geometry arising from overlapping volumes and print the volumes as one. This may cause internal cavities to disappear.", + "type": "bool", + "default_value": true + }, + "meshfix_union_all_remove_holes": + { + "label": "Remove All Holes", + "description": "Remove the holes in each layer and keep only the outside shape. This will ignore any invisible internal geometry. However, it also ignores layer holes which can be viewed from above or below.", + "type": "bool", + "default_value": false + }, + "meshfix_extensive_stitching": + { + "label": "Extensive Stitching", + "description": "Extensive stitching tries to stitch up open holes in the mesh by closing the hole with touching polygons. This option can introduce a lot of processing time.", + "type": "bool", + "default_value": false + }, + "meshfix_keep_open_polygons": + { + "label": "Keep Disconnected Faces", + "description": "Normally Cura tries to stitch up small holes in the mesh and remove parts of a layer with big holes. Enabling this option keeps those parts which cannot be stitched. This option should be used as a last resort option when everything else fails to produce proper GCode.", + "type": "bool", + "default_value": false + } + } + }, + "blackmagic": + { + "label": "Special Modes", + "type": "category", + "icon": "category_blackmagic", + "description": "category_blackmagic", + "children": + { + "print_sequence": + { + "label": "Print Sequence", + "description": "Whether to print all objects one layer at a time or to wait for one object to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.", + "type": "enum", + "options": + { + "all_at_once": "All at Once", + "one_at_a_time": "One at a Time" + }, + "default_value": "all_at_once", + "global_only": true + }, + "magic_mesh_surface_mode": + { + "label": "Surface Mode", + "description": "Treat the model as a surface only, a volume, or volumes with loose surfaces. The normal print mode only prints enclosed volumes. \"Surface\" prints a single wall tracing the mesh surface with no infill and no top/bottom skin. \"Both\" prints enclosed volumes like normal and any remaining polygons as surfaces.", + "type": "enum", + "options": + { + "normal": "Normal", + "surface": "Surface", + "both": "Both" + }, + "default_value": "normal" + }, + "magic_spiralize": + { + "label": "Spiralize Outer Contour", + "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid object into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", + "type": "bool", + "default_value": false, + "global_only": "True" + } + } + }, + "dual": + { + "label": "Dual Extrusion", + "type": "category", + "icon": "category_dual", + "description": "Settings used for printing with multiple extruders.", + "children": + { + "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_value": 0, + "minimum_value": "0", + "maximum_value": "machine_extruder_count - 1", + "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_value": 0, + "minimum_value": "0", + "maximum_value": "machine_extruder_count - 1", + "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_value": 0, + "value": "support_extruder_nr", + "minimum_value": "0", + "maximum_value": "machine_extruder_count - 1", + "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_value": 0, + "value": "support_extruder_nr", + "minimum_value": "0", + "maximum_value": "machine_extruder_count - 1", + "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_value": 0, + "value": "support_extruder_nr", + "minimum_value": "0", + "maximum_value": "machine_extruder_count - 1", + "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": "bool", + "default_value": false, + "global_only": "True" + }, + "prime_tower_size": + { + "label": "Prime Tower Size", + "description": "The width of the prime tower.", + "type": "float", + "unit": "mm", + "enabled": "prime_tower_enable", + "default_value": 15, + "value": "15 if prime_tower_enable else 0", + "minimum_value": "0", + "maximum_value_warning": "20", + "global_only": "True" + }, + "prime_tower_position_x": + { + "label": "Prime Tower X Position", + "description": "The x coordinate of the position of the prime tower.", + "type": "float", + "unit": "mm", + "enabled": "prime_tower_enable", + "default_value": 200, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "global_only": "True" + }, + "prime_tower_position_y": + { + "label": "Prime Tower Y Position", + "description": "The y coordinate of the position of the prime tower.", + "type": "float", + "unit": "mm", + "enabled": "prime_tower_enable", + "default_value": 200, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "global_only": "True" + }, + "prime_tower_flow": + { + "label": "Prime Tower Flow", + "description": "Flow compensation: the amount of material extruded is multiplied by this value.", + "type": "float", + "unit": "%", + "enabled": "prime_tower_enable", + "default_value": 100, + "minimum_value": "0.0001", + "minimum_value_warning": "50", + "maximum_value_warning": "150", + "global_only": "True" + }, + "prime_tower_wipe_enabled": + { + "label": "Wipe Nozzle on Prime Tower", + "description": "After printing the prime tower with one nozzle, wipe the oozed material from the other nozzle off on the prime tower.", + "type": "bool", + "enabled": "prime_tower_enable", + "default_value": false, + "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.", + "type": "float", + "unit": "mm", + "default_value": 0.15, + "minimum_value": "0", + "maximum_value_warning": "1.0" + }, + "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": "bool", + "default_value": 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.", + "type": "float", + "unit": "°", + "enabled": "ooze_shield_enabled", + "default_value": 60, + "minimum_value": "0", + "maximum_value": "90", + "global_only": "True" + }, + "ooze_shield_dist": + { + "label": "Ooze Shield Distance", + "description": "Distance of the ooze shield from the print, in the X/Y directions.", + "type": "float", + "unit": "mm", + "enabled": "ooze_shield_enabled", + "default_value": 2, + "minimum_value": "0", + "maximum_value_warning": "30", + "global_only": "True" + } + } + }, + "experimental": + { + "label": "Experimental Modes", + "type": "category", + "icon": "category_blackmagic", + "description": "experimental!", + "children": + { + "draft_shield_enabled": + { + "label": "Enable Draft Shield", + "description": "This will create a wall around the object, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.", + "type": "bool", + "default_value": false, + "global_only": true + }, + "draft_shield_dist": + { + "label": "Draft Shield X/Y Distance", + "description": "Distance of the draft shield from the print, in the X/Y directions.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "100", + "default_value": 10, + "enabled": "draft_shield_enabled", + "global_only": true + }, + "draft_shield_height_limitation": + { + "label": "Draft Shield Limitation", + "description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the object or at a limited height.", + "type": "enum", + "options": + { + "full": "Full", + "limited": "Limited" + }, + "default_value": "full", + "enabled": "draft_shield_enabled", + "global_only": true + }, + "draft_shield_height": + { + "label": "Draft Shield Height", + "description": "Height limitation of the draft shield. Above this height no draft shield will be printed.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "maximum_value_warning": "9999", + "default_value": 0, + "value": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0", + "enabled": "draft_shield_height_limitation == \"limited\"", + "global_only": true + }, + "conical_overhang_enabled": { + "label": "Make Overhang Printable", + "description": "Change the geometry of the printed model such that minimal support is required. Steep overhangs will become shallow overhangs. Overhanging areas will drop down to become more vertical.", + "type": "bool", + "default_value": false + }, + "conical_overhang_angle": { + "label": "Maximum Model Angle", + "description": "The maximum angle of overhangs after the they have been made printable. At a value of 0° all overhangs are replaced by a piece of model connected to the build plate, 90° will not change the model in any way.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "maximum_value": "89", + "default_value": 50, + "enabled": "conical_overhang_enabled" + }, + "coasting_enable": + { + "label": "Enable Coasting", + "description": "Coasting replaces the last part of an extrusion path with a travel path. The oozed material is used to print the last piece of the extrusion path in order to reduce stringing.", + "type": "bool", + "default_value": false, + "global_only": true + }, + "coasting_volume": + { + "label": "Coasting Volume", + "description": "The volume otherwise oozed. This value should generally be close to the nozzle diameter cubed.", + "unit": "mm³", + "type": "float", + "default_value": 0.064, + "minimum_value": "0", + "maximum_value_warning": "2.0", + "enabled": "coasting_enable", + "global_only": true + }, + "coasting_min_volume": + { + "label": "Minimum Volume Before Coasting", + "description": "The smallest volume an extrusion path should have before allowing coasting. For smaller extrusion paths, less pressure has been built up in the bowden tube and so the coasted volume is scaled linearly. This value should always be larger than the Coasting Volume.", + "unit": "mm³", + "type": "float", + "default_value": 0.8, + "minimum_value": "0", + "maximum_value_warning": "10.0", + "enabled": "coasting_enable", + "global_only": true + }, + "coasting_speed": + { + "label": "Coasting Speed", + "description": "The speed by which to move during coasting, relative to the speed of the extrusion path. A value slightly under 100% is advised, since during the coasting move the pressure in the bowden tube drops.", + "unit": "%", + "type": "float", + "default_value": 90, + "minimum_value": "0.0001", + "maximum_value_warning": "100", + "enabled": "coasting_enable", + "global_only": true + }, + "skin_outline_count": + { + "label": "Extra Skin Wall Count", + "description": "Replaces the outermost part of the top/bottom pattern with a number of concentric lines. Using one or two lines improves roofs that start on infill material.", + "default_value": 0, + "minimum_value": "0", + "maximum_value_warning": "10", + "type": "int" + }, + "skin_alternate_rotation": + { + "label": "Alternate Skin Rotation", + "description": "Alternate the direction in which the top/bottom layers are printed. Normally they are printed diagonally only. This setting adds the X-only and Y-only directions.", + "type": "bool", + "default_value": false, + "enabled": "top_bottom_pattern != \"concentric\"" + }, + "support_conical_enabled": + { + "label": "Enable Conical Support", + "description": "Experimental feature: Make support areas smaller at the bottom than at the overhang.", + "type": "bool", + "default_value": false, + "enabled": "support_enable" + }, + "support_conical_angle": + { + "label": "Conical Support Angle", + "description": "The angle of the tilt of conical support. With 0 degrees being vertical, and 90 degrees being horizontal. Smaller angles cause the support to be more sturdy, but consist of more material. Negative angles cause the base of the support to be wider than the top.", + "unit": "°", + "type": "float", + "minimum_value": "-90", + "minimum_value_warning": "-45", + "maximum_value_warning": "45", + "maximum_value": "90", + "default_value": 30, + "enabled": "support_conical_enabled and support_enable" + }, + "support_conical_min_width": + { + "label": "Conical Support Minimum Width", + "description": "Minimum width to which the base of the conical support area is reduced. Small widths can lead to unstable support structures.", + "unit": "mm", + "default_value": 5.0, + "minimum_value": "0", + "minimum_value_warning": "machine_nozzle_size * 3", + "maximum_value_warning": "100.0", + "type": "float", + "enabled": "support_conical_enabled and support_enable" + }, + "magic_fuzzy_skin_enabled": + { + "label": "Fuzzy Skin", + "description": "Randomly jitter while printing the outer wall, so that the surface has a rough and fuzzy look.", + "type": "bool", + "default_value": false + }, + "magic_fuzzy_skin_thickness": + { + "label": "Fuzzy Skin Thickness", + "description": "The width within which to jitter. It's advised to keep this below the outer wall width, since the inner walls are unaltered.", + "type": "float", + "unit": "mm", + "default_value": 0.3, + "minimum_value": "0.001", + "maximum_value_warning": "wall_line_width_0", + "enabled": "magic_fuzzy_skin_enabled" + }, + "magic_fuzzy_skin_point_density": + { + "label": "Fuzzy Skin Density", + "description": "The average density of points introduced on each polygon in a layer. Note that the original points of the polygon are discarded, so a low density results in a reduction of the resolution.", + "type": "float", + "unit": "1/mm", + "default_value": 1.25, + "minimum_value": "0.008", + "minimum_value_warning": "0.1", + "maximum_value_warning": "10", + "maximum_value": "2 / magic_fuzzy_skin_thickness", + "enabled": "magic_fuzzy_skin_enabled", + "children": + { + "magic_fuzzy_skin_point_dist": + { + "label": "Fuzzy Skin Point Distance", + "description": "The average distance between the random points introduced on each line segment. Note that the original points of the polygon are discarded, so a high smoothness results in a reduction of the resolution. This value must be higher than half the Fuzzy Skin Thickness.", + "type": "float", + "unit": "mm", + "default_value": 0.8, + "minimum_value": "magic_fuzzy_skin_thickness / 2", + "minimum_value_warning": "0.1", + "maximum_value_warning": "10", + "value": "10000 if magic_fuzzy_skin_point_density == 0 else 1 / magic_fuzzy_skin_point_density", + "enabled": "magic_fuzzy_skin_enabled" + } + } + }, + "wireframe_enabled": + { + "label": "Wire Printing", + "description": "Print only the outside surface with a sparse webbed structure, printing 'in thin air'. This is realized by horizontally printing the contours of the model at given Z intervals which are connected via upward and diagonally downward lines.", + "type": "bool", + "default_value": false, + "global_only": "True" + }, + "wireframe_height": + { + "label": "WP Connection Height", + "description": "The height of the upward and diagonally downward lines between two horizontal parts. This determines the overall density of the net structure. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 3, + "minimum_value": "0.0001", + "maximum_value_warning": "20", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_roof_inset": + { + "label": "WP Roof Inset Distance", + "description": "The distance covered when making a connection from a roof outline inward. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 3, + "minimum_value": "0", + "minimum_value_warning": "machine_nozzle_size", + "maximum_value_warning": "20", + "enabled": "wireframe_enabled", + "value": "wireframe_height", + "global_only": "True" + }, + "wireframe_printspeed": + { + "label": "WP Speed", + "description": "Speed at which the nozzle moves when extruding material. Only applies to Wire Printing.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "50", + "enabled": "wireframe_enabled", + "global_only": "True", + "children": + { + "wireframe_printspeed_bottom": + { + "label": "WP Bottom Printing Speed", + "description": "Speed of printing the first layer, which is the only layer touching the build platform. Only applies to Wire Printing.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "50", + "enabled": "wireframe_enabled", + "global_only": "True", + "value": "wireframe_printspeed" + }, + "wireframe_printspeed_up": + { + "label": "WP Upward Printing Speed", + "description": "Speed of printing a line upward 'in thin air'. Only applies to Wire Printing.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "50", + "enabled": "wireframe_enabled", + "global_only": "True", + "value": "wireframe_printspeed" + }, + "wireframe_printspeed_down": + { + "label": "WP Downward Printing Speed", + "description": "Speed of printing a line diagonally downward. Only applies to Wire Printing.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "50", + "enabled": "wireframe_enabled", + "global_only": "True", + "value": "wireframe_printspeed" + }, + "wireframe_printspeed_flat": + { + "label": "WP Horizontal Printing Speed", + "description": "Speed of printing the horizontal contours of the object. Only applies to Wire Printing.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "100", + "value": "wireframe_printspeed", + "enabled": "wireframe_enabled", + "global_only": "True" + } + } + }, + "wireframe_flow": + { + "label": "WP Flow", + "description": "Flow compensation: the amount of material extruded is multiplied by this value. Only applies to Wire Printing.", + "unit": "%", + "default_value": 100, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "float", + "enabled": "wireframe_enabled", + "global_only": "True", + "children": + { + "wireframe_flow_connection": + { + "label": "WP Connection Flow", + "description": "Flow compensation when going up or down. Only applies to Wire Printing.", + "unit": "%", + "default_value": 100, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "float", + "enabled": "wireframe_enabled", + "global_only": "True", + "value": "wireframe_flow" + }, + "wireframe_flow_flat": + { + "label": "WP Flat Flow", + "description": "Flow compensation when printing flat lines. Only applies to Wire Printing.", + "unit": "%", + "default_value": 100, + "minimum_value": "0", + "maximum_value_warning": "100", + "type": "float", + "enabled": "wireframe_enabled", + "global_only": "True", + "value": "wireframe_flow" + } + } + }, + "wireframe_top_delay": + { + "label": "WP Top Delay", + "description": "Delay time after an upward move, so that the upward line can harden. Only applies to Wire Printing.", + "unit": "sec", + "type": "float", + "default_value": 0, + "minimum_value": "0", + "maximum_value_warning": "1", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_bottom_delay": + { + "label": "WP Bottom Delay", + "description": "Delay time after a downward move. Only applies to Wire Printing.", + "unit": "sec", + "type": "float", + "default_value": 0, + "minimum_value": "0", + "maximum_value_warning": "1", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_flat_delay": + { + "label": "WP Flat Delay", + "description": "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too long delays cause sagging. Only applies to Wire Printing.", + "unit": "sec", + "type": "float", + "default_value": 0.1, + "minimum_value": "0", + "maximum_value_warning": "0.5", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_up_half_speed": + { + "label": "WP Ease Upward", + "description": "Distance of an upward move which is extruded with half speed.\nThis can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 0.3, + "minimum_value": "0", + "maximum_value_warning": "5.0", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_top_jump": + { + "label": "WP Knot Size", + "description": "Creates a small knot at the top of an upward line, so that the consecutive horizontal layer has a better chance to connect to it. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 0.6, + "minimum_value": "0", + "maximum_value_warning": "2.0", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_fall_down": + { + "label": "WP Fall Down", + "description": "Distance with which the material falls down after an upward extrusion. This distance is compensated for. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 0.5, + "minimum_value": "0", + "maximum_value_warning": "wireframe_height", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_drag_along": + { + "label": "WP Drag Along", + "description": "Distance with which the material of an upward extrusion is dragged along with the diagonally downward extrusion. This distance is compensated for. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 0.6, + "minimum_value": "0", + "maximum_value_warning": "wireframe_height", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_strategy": + { + "label": "WP Strategy", + "description": "Strategy for making sure two consecutive layers connect at each connection point. Retraction lets the upward lines harden in the right position, but may cause filament grinding. A knot can be made at the end of an upward line to heighten the chance of connecting to it and to let the line cool; however, it may require slow printing speeds. Another strategy is to compensate for the sagging of the top of an upward line; however, the lines won't always fall down as predicted.", + "type": "enum", + "options": + { + "compensate": "Compensate", + "knot": "Knot", + "retract": "Retract" + }, + "default_value": "compensate", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_straight_before_down": + { + "label": "WP Straighten Downward Lines", + "description": "Percentage of a diagonally downward line which is covered by a horizontal line piece. This can prevent sagging of the top most point of upward lines. Only applies to Wire Printing.", + "type": "float", + "unit": "%", + "default_value": 20, + "minimum_value": "0", + "maximum_value": "100", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_roof_fall_down": + { + "label": "WP Roof Fall Down", + "description": "The distance which horizontal roof lines printed 'in thin air' fall down when being printed. This distance is compensated for. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 2, + "minimum_value_warning": "0", + "maximum_value_warning": "wireframe_roof_inset", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_roof_drag_along": + { + "label": "WP Roof Drag Along", + "description": "The distance of the end piece of an inward line which gets dragged along when going back to the outer outline of the roof. This distance is compensated for. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 0.8, + "minimum_value": "0", + "maximum_value_warning": "10", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_roof_outer_delay": + { + "label": "WP Roof Outer Delay", + "description": "Time spent at the outer perimeters of hole which is to become a roof. Longer times can ensure a better connection. Only applies to Wire Printing.", + "type": "float", + "unit": "sec", + "default_value": 0.2, + "minimum_value": "0", + "maximum_value_warning": "2.0", + "enabled": "wireframe_enabled", + "global_only": "True" + }, + "wireframe_nozzle_clearance": + { + "label": "WP Nozzle Clearance", + "description": "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 less upward connections with the next layer. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default_value": 1, + "minimum_value_warning": "0", + "maximum_value_warning": "10.0", + "enabled": "wireframe_enabled", + "global_only": "True" + } + } + } + } +} diff --git a/resources/definitions/grr_neo.def.json b/resources/definitions/grr_neo.def.json new file mode 100644 index 0000000000..563bdd1a67 --- /dev/null +++ b/resources/definitions/grr_neo.def.json @@ -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" + } + } +} \ No newline at end of file diff --git a/resources/definitions/innovo_inventor.def.json b/resources/definitions/innovo_inventor.def.json new file mode 100644 index 0000000000..9ba8d0d44b --- /dev/null +++ b/resources/definitions/innovo_inventor.def.json @@ -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 + } + } +} \ No newline at end of file diff --git a/resources/definitions/m180.def.json b/resources/definitions/m180.def.json new file mode 100644 index 0000000000..bb027d33bc --- /dev/null +++ b/resources/definitions/m180.def.json @@ -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" + } + } +} \ No newline at end of file diff --git a/resources/definitions/maker_starter.def.json b/resources/definitions/maker_starter.def.json new file mode 100644 index 0000000000..a26ca058f0 --- /dev/null +++ b/resources/definitions/maker_starter.def.json @@ -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 + } + } +} \ No newline at end of file diff --git a/resources/definitions/prusa_i3.def.json b/resources/definitions/prusa_i3.def.json new file mode 100644 index 0000000000..12e7054694 --- /dev/null +++ b/resources/definitions/prusa_i3.def.json @@ -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" + } + } +} \ No newline at end of file diff --git a/resources/definitions/prusa_i3_xl.def.json b/resources/definitions/prusa_i3_xl.def.json new file mode 100644 index 0000000000..819a93b860 --- /dev/null +++ b/resources/definitions/prusa_i3_xl.def.json @@ -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" + } + } +} \ No newline at end of file diff --git a/resources/definitions/rigidbot.def.json b/resources/definitions/rigidbot.def.json new file mode 100644 index 0000000000..ace8300d5d --- /dev/null +++ b/resources/definitions/rigidbot.def.json @@ -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\"" + } + } +} diff --git a/resources/definitions/rigidbot_big.def.json b/resources/definitions/rigidbot_big.def.json new file mode 100644 index 0000000000..ce27d36745 --- /dev/null +++ b/resources/definitions/rigidbot_big.def.json @@ -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\"" + } + } +} \ No newline at end of file diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json new file mode 100644 index 0000000000..60e4dbbf40 --- /dev/null +++ b/resources/definitions/ultimaker.def.json @@ -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" + } +} diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json new file mode 100644 index 0000000000..7b2222e5b3 --- /dev/null +++ b/resources/definitions/ultimaker2.def.json @@ -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" + } + } +} diff --git a/resources/definitions/ultimaker2_extended.def.json b/resources/definitions/ultimaker2_extended.def.json new file mode 100644 index 0000000000..e3c7d1fd01 --- /dev/null +++ b/resources/definitions/ultimaker2_extended.def.json @@ -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 + } + } +} diff --git a/resources/definitions/ultimaker2_extended_plus.def.json b/resources/definitions/ultimaker2_extended_plus.def.json new file mode 100644 index 0000000000..eae48773b6 --- /dev/null +++ b/resources/definitions/ultimaker2_extended_plus.def.json @@ -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 + } + } +} diff --git a/resources/definitions/ultimaker2_go.def.json b/resources/definitions/ultimaker2_go.def.json new file mode 100644 index 0000000000..d3ef53d633 --- /dev/null +++ b/resources/definitions/ultimaker2_go.def.json @@ -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]] + ] + } + } +} diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json new file mode 100644 index 0000000000..9f68c2a16b --- /dev/null +++ b/resources/definitions/ultimaker2_plus.def.json @@ -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]] + ] + } + } +} diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json new file mode 100644 index 0000000000..1dbdb95d8c --- /dev/null +++ b/resources/definitions/ultimaker_original.def.json @@ -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" + } + } +} diff --git a/resources/definitions/ultimaker_original_plus.def.json b/resources/definitions/ultimaker_original_plus.def.json new file mode 100644 index 0000000000..830050beb0 --- /dev/null +++ b/resources/definitions/ultimaker_original_plus.def.json @@ -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 + } + } +} diff --git a/resources/definitions/uniqbot_one.def.json b/resources/definitions/uniqbot_one.def.json new file mode 100644 index 0000000000..1342e065a2 --- /dev/null +++ b/resources/definitions/uniqbot_one.def.json @@ -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" + } + } +} diff --git a/resources/i18n/de/fdmprinter.json.po b/resources/i18n/de/fdmprinter.json.po index 9ba0fdca1f..e9a04a21ff 100644 --- a/resources/i18n/de/fdmprinter.json.po +++ b/resources/i18n/de/fdmprinter.json.po @@ -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." diff --git a/resources/i18n/en/fdmprinter.json.po b/resources/i18n/en/fdmprinter.json.po index 9ee03631bb..4578306bfb 100644 --- a/resources/i18n/en/fdmprinter.json.po +++ b/resources/i18n/en/fdmprinter.json.po @@ -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." diff --git a/resources/i18n/es/fdmprinter.json.po b/resources/i18n/es/fdmprinter.json.po index a3e79fa69a..2c1f044147 100644 --- a/resources/i18n/es/fdmprinter.json.po +++ b/resources/i18n/es/fdmprinter.json.po @@ -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." diff --git a/resources/i18n/fdmprinter.json.pot b/resources/i18n/fdmprinter.json.pot index b7c6f07b46..b8870ef252 100644 --- a/resources/i18n/fdmprinter.json.pot +++ b/resources/i18n/fdmprinter.json.pot @@ -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 "" diff --git a/resources/i18n/fi/fdmprinter.json.po b/resources/i18n/fi/fdmprinter.json.po index ad12060cea..cc402b0e54 100644 --- a/resources/i18n/fi/fdmprinter.json.po +++ b/resources/i18n/fi/fdmprinter.json.po @@ -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." diff --git a/resources/i18n/fr/fdmprinter.json.po b/resources/i18n/fr/fdmprinter.json.po index 7a49440b04..f8e10fa5f4 100644 --- a/resources/i18n/fr/fdmprinter.json.po +++ b/resources/i18n/fr/fdmprinter.json.po @@ -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." diff --git a/resources/i18n/it/fdmprinter.json.po b/resources/i18n/it/fdmprinter.json.po index b0e04c4308..40b5e36916 100644 --- a/resources/i18n/it/fdmprinter.json.po +++ b/resources/i18n/it/fdmprinter.json.po @@ -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à." diff --git a/resources/i18n/nl/fdmprinter.json.po b/resources/i18n/nl/fdmprinter.json.po index ff8f896a74..2c6e8a1df8 100644 --- a/resources/i18n/nl/fdmprinter.json.po +++ b/resources/i18n/nl/fdmprinter.json.po @@ -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." diff --git a/resources/instances/high_quality.inst.cfg b/resources/instances/high_quality.inst.cfg new file mode 100644 index 0000000000..2e860cf380 --- /dev/null +++ b/resources/instances/high_quality.inst.cfg @@ -0,0 +1,9 @@ +[general] +version = 2 +name = high +definition = fdmprinter + +[metadata] +type = quality + +[values] diff --git a/resources/instances/normal_quality.inst.cfg b/resources/instances/normal_quality.inst.cfg new file mode 100644 index 0000000000..6bb23d841c --- /dev/null +++ b/resources/instances/normal_quality.inst.cfg @@ -0,0 +1,9 @@ +[general] +version = 2 +name = normal +definition = fdmprinter + +[metadata] +type = quality + +[values] diff --git a/resources/machines/RigidBot.json b/resources/machines/RigidBot.json deleted file mode 100644 index 9b19c0871e..0000000000 --- a/resources/machines/RigidBot.json +++ /dev/null @@ -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" } } - } -} diff --git a/resources/machines/RigidBotBig.json b/resources/machines/RigidBotBig.json deleted file mode 100644 index 20b1ea2982..0000000000 --- a/resources/machines/RigidBotBig.json +++ /dev/null @@ -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" } } - } -} diff --git a/resources/machines/bq_hephestos.json b/resources/machines/bq_hephestos.json deleted file mode 100644 index 01aac77f0d..0000000000 --- a/resources/machines/bq_hephestos.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/bq_hephestos_2.json b/resources/machines/bq_hephestos_2.json deleted file mode 100644 index cc87057cc5..0000000000 --- a/resources/machines/bq_hephestos_2.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/bq_hephestos_xl.json b/resources/machines/bq_hephestos_xl.json deleted file mode 100644 index 8a8ad162ce..0000000000 --- a/resources/machines/bq_hephestos_xl.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/bq_witbox.json b/resources/machines/bq_witbox.json deleted file mode 100644 index e12c812fc0..0000000000 --- a/resources/machines/bq_witbox.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/bq_witbox_2.json b/resources/machines/bq_witbox_2.json deleted file mode 100644 index 4eb9b67d99..0000000000 --- a/resources/machines/bq_witbox_2.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/dual_extrusion_printer.json b/resources/machines/dual_extrusion_printer.json deleted file mode 100644 index f85cce9081..0000000000 --- a/resources/machines/dual_extrusion_printer.json +++ /dev/null @@ -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 - } - } - } - } - } - } -} diff --git a/resources/machines/fdmprinter.json b/resources/machines/fdmprinter.json deleted file mode 100644 index 9ce745e2b0..0000000000 --- a/resources/machines/fdmprinter.json +++ /dev/null @@ -1,2349 +0,0 @@ -{ - "id": "fdmprinter", - "visible": false, - "version": 1, - "name": "FDM Printer Base Description", - "author": "Ultimaker B.V.", - "manufacturer": "Ultimaker", - "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g", - - "add_pages": [], - - "machine_settings": { - "machine_show_variants": { - "description": "Wether to show the different variants of this machine, which are described in separate json files.", - "default": false - }, - "machine_start_gcode": { - "description": "Gcode commands to be executed at the very start - separated by \\n.", - "default": "G28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0", - "global_only": true - }, - "machine_end_gcode": { - "description": "Gcode commands to be executed at the very end - separated by \\n.", - "default": "M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84", - "global_only": true - }, - "material_bed_temp_wait": { - "description": "Whether to insert a command to wait until the bed temperature is reached at the start.", - "default": true, - "global_only": true - }, - "material_print_temp_wait": { - "description": "Whether to insert a command to wait until the nozzle temperatures are reached at the start.", - "default": true, - "global_only": true - }, - "material_print_temp_prepend": { - "description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.", - "default": true, - "global_only": true - }, - "material_bed_temp_prepend": { - "description": "Whether to include bed temperature commands at the start of the gcode. When the start_gcode already contains bed temperature commands Cura frontend will automatically disable this setting.", - "default": true, - "global_only": true - }, - "machine_width": { - "description": "The width (X-direction) of the printable area.", - "default": 100, - "global_only": true - }, - "machine_depth": { - "description": "The depth (Y-direction) of the printable area.", - "default": 100, - "global_only": true - }, - "machine_height": { - "description": "The height (Z-direction) of the printable area.", - "default": 100, - "global_only": true - }, - "machine_heated_bed": { - "description": "Whether the machine has a heated bed present.", - "default": false, - "global_only": true - }, - "machine_center_is_zero": { - "description": "Whether the X/Y coordinates of the zero position of the printer is at the center of the printable area.", - "default": false, - "global_only": true - }, - "machine_extruder_count": { - "description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.", - "default": 1, - "global_only": true - }, - "machine_nozzle_tip_outer_diameter": { - "description": "The outer diameter of the tip of the nozzle.", - "default": 1, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_nozzle_head_distance": { - "description": "The height difference between the tip of the nozzle and the lowest part of the print head.", - "default": 3, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_nozzle_expansion_angle": { - "description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.", - "default": 45, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_heat_zone_length": { - "description": "The distance from the tip of the nozzle in which heat from the nozzle is transfered to the filament.", - "default": 16, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_nozzle_heat_up_speed": { - "description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.", - "default": 2.0, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_nozzle_cool_down_speed": { - "description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.", - "default": 2.0, - "SEE_machine_extruder_trains": true, - "global_only": true - }, - "machine_gcode_flavor": { - "description": "The type of gcode to be generated.", - "default": "RepRap", - "global_only": true - }, - "machine_disallowed_areas": { - "description": "A list of polygons with areas the print head is not allowed to enter.", - "type": "polygons", - "default": [], - "global_only": true - }, - "machine_platform_offset": { - "description": "Where to display the platform mesh.", - "default": [ - 0, - 0, - 0 - ], - "global_only": true - }, - "machine_head_polygon": { - "description": "A 2D silhouette of the print head (fan caps excluded).", - "type": "polygon", - "default": [ - [ - -1, - 1 - ], - [ - -1, - -1 - ], - [ - 1, - -1 - ], - [ - 1, - 1 - ] - ], - "global_only": true - }, - "machine_head_with_fans_polygon": { - "description": "A 2D silhouette of the print head (fan caps included).", - "type": "polygon", - "default": [ - [ - -20, - 10 - ], - [ - 10, - 10 - ], - [ - 10, - -10 - ], - [ - -20, - -10 - ] - ], - "global_only": true - }, - "gantry_height": { - "description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).", - "default": 99999999999, - "global_only": true - } - }, - "categories": { - "machine": { - "label": "Machine", - "visible": true, - "icon": "category_machine", - "settings": { - "machine_nozzle_size": { - "label": "Nozzle Diameter", - "description": "The inner diameter of the nozzle. Change this setting when using a non-standard nozzle size.", - "unit": "mm", - "type": "float", - "default": 0.4, - "min_value": "0.001", - "max_value_warning": "10", - "visible": false - } - }, - "global_only": true - }, - "resolution": { - "label": "Quality", - "visible": true, - "icon": "category_layer_height", - "settings": { - "layer_height": { - "label": "Layer Height", - "description": "The height of each layer in mm. Higher values produce faster prints in lower resolution, lower values produce slower prints in higher resolution.", - "unit": "mm", - "type": "float", - "default": 0.1, - "min_value": "0.001", - "min_value_warning": "0.04", - "max_value_warning": "0.8 * machine_nozzle_size", - "global_only": "True" - }, - "layer_height_0": { - "label": "Initial Layer Height", - "description": "The height of the initial layer in mm. A thicker initial layer makes adhesion to the build plate easier.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": "0.001", - "min_value_warning": "0.04", - "max_value_warning": "0.8 * machine_nozzle_size", - "visible": false, - "global_only": "True" - }, - "line_width": { - "label": "Line Width", - "description": "Width of a single line. Generally, the width of each line should correspond to the width of the nozzle. However, slightly reducing this value could produce better prints.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "2 * machine_nozzle_size", - "default": 0.4, - "type": "float", - "visible": false, - "inherit_function": "machine_nozzle_size", - "children": { - "wall_line_width": { - "label": "Wall Line Width", - "description": "Width of a single wall line.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false, - "children": { - "wall_line_width_0": { - "label": "Outer Wall Line Width", - "description": "Width of the outermost wall line. By lowering this value, higher levels of detail can be printed.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false - }, - "wall_line_width_x": { - "label": "Inner Wall(s) Line Width", - "description": "Width of a single wall line for all wall lines except the outermost one.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false - } - } - }, - "skin_line_width": { - "label": "Top/bottom Line Width", - "description": "Width of a single top/bottom line.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false - }, - "infill_line_width": { - "label": "Infill Line Width", - "description": "Width of a single infill line.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false - }, - "skirt_line_width": { - "label": "Skirt Line Width", - "description": "Width of a single skirt line.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false, - "global_only": true - }, - "support_line_width": { - "label": "Support Line Width", - "description": "Width of a single support structure line.", - "unit": "mm", - "min_value": "0.0001", - "min_value_warning": "0.2", - "max_value_warning": "5", - "default": 0.4, - "type": "float", - "visible": false, - "enabled": "support_enable", - "global_only": true - }, - "support_roof_line_width": { - "label": "Support Roof Line Width", - "description": "Width of a single support roof line.", - "unit": "mm", - "default": 0.4, - "min_value": "0.0001", - "max_value_warning": "machine_nozzle_size * 2", - "type": "float", - "visible": false, - "enabled": "support_roof_enable", - "global_only": true - } - } - } - } - }, - "shell": { - "label": "Shell", - "visible": true, - "icon": "category_shell", - "settings": { - "wall_thickness": { - "label": "Wall Thickness", - "description": "The thickness of the outside walls in the horizontal direction. This value divided by the wall line width defines the number of walls.", - "unit": "mm", - "default": 0.8, - "min_value": "0", - "min_value_warning": "line_width", - "max_value_warning": "5 * line_width", - "type": "float", - "visible": true, - "children": { - "wall_line_count": { - "label": "Wall Line Count", - "description": "The number of walls. When calculated by the wall thickness, this value is rounded to a whole number.", - "default": 2, - "min_value": "0", - "type": "int", - "visible": false, - "inherit_function": "1 if magic_spiralize else max(1, round((wall_thickness - wall_line_width_0) / wall_line_width_x) + 1)" - } - } - }, - "top_bottom_thickness": { - "label": "Top/Bottom Thickness", - "description": "The thickness of the top/bottom layers in the print. This value divided by the layer height defines the number of top/bottom layers.", - "unit": "mm", - "default": 0.8, - "min_value": "0", - "max_value": "5", - "min_value_warning": "0.6", - "type": "float", - "visible": true, - "children": { - "top_thickness": { - "label": "Top Thickness", - "description": "The thickness of the top layers in the print. This value divided by the layer height defines the number of top layers.", - "unit": "mm", - "default": 0.8, - "min_value": "0", - "max_value_warning": "100", - "type": "float", - "visible": false, - "children": { - "top_layers": { - "label": "Top Layers", - "description": "The number of top layers. When calculated by the top thickness, this value is rounded to a whole number.", - "default": 8, - "min_value": "0", - "max_value_warning": "100", - "type": "int", - "visible": false, - "inherit_function": "0 if infill_sparse_density == 100 else math.ceil(round(parent_value / layer_height, 4))" - } - } - }, - "bottom_thickness": { - "label": "Bottom Thickness", - "description": "The thickness of the bottom layers in the print. This value divided by the layer height defines the number of bottom layers.", - "unit": "mm", - "default": 0.6, - "min_value": "0", - "type": "float", - "visible": false, - "children": { - "bottom_layers": { - "label": "Bottom Layers", - "description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.", - "min_value": "0", - "default": 6, - "type": "int", - "visible": false, - "inherit_function": "999999 if infill_sparse_density == 100 else math.ceil(round(parent_value / layer_height, 4))" - } - } - } - } - }, - "top_bottom_pattern": { - "label": "Top/Bottom Pattern", - "description": "The pattern of the top/bottom layers.", - "type": "enum", - "options": { - "lines": "Lines", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default": "lines", - "visible": false - }, - "wall_0_inset": { - "label": "Outer Wall Inset", - "description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the object.", - "unit": "mm", - "type": "float", - "default": 0.0, - "inherit_function": "(machine_nozzle_size - wall_line_width_0) / 2 if wall_line_width_0 < machine_nozzle_size else 0", - "min_value_warning": "0", - "max_value_warning": "machine_nozzle_size", - "visible": false - }, - "alternate_extra_perimeter": { - "label": "Alternate Extra Wall", - "description": "Prints an extra wall at every other layer. This way infill gets caught between these extra walls, resulting in stronger prints.", - "type": "boolean", - "default": false, - "visible": false, - "inherit": false - }, - "remove_overlapping_walls_enabled": { - "label": "Remove Overlapping Wall Parts", - "description": "Remove parts of a wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin parts and sharp corners in models.", - "type": "boolean", - "default": false, - "visible": false, - "enabled": "False", - "children": { - "remove_overlapping_walls_0_enabled": { - "label": "Remove Overlapping Outer Wall Parts", - "description": "Remove parts of an outer wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin pieces in a model and sharp corners.", - "type": "boolean", - "default": false, - "visible": false, - "inherit": true, - "enabled": "False" - }, - "remove_overlapping_walls_x_enabled": { - "label": "Remove Overlapping Inner Wall Parts", - "description": "Remove parts of an inner wall that would otherwise overlap and cause over-extrusion. These overlaps occur in thin pieces in a model and sharp corners.", - "type": "boolean", - "default": true, - "visible": false, - "inherit": false - } - } - }, - "fill_perimeter_gaps": { - "label": "Fill Gaps Between Walls", - "description": "Fills the gaps between walls when overlapping inner wall parts are removed.", - "type": "enum", - "options": { - "nowhere": "Nowhere", - "everywhere": "Everywhere", - "skin": "Skin" - }, - "default": "everywhere", - "visible": false, - "enabled": "remove_overlapping_walls_x_enabled" - }, - "travel_compensate_overlapping_walls_enabled": { - "label": "Compensate Wall Overlaps", - "description": "Compensate the flow for parts of a wall being printed where there is already a wall in place.", - "type": "boolean", - "default": true, - "visible": false - }, - "xy_offset": { - "label": "Horizontal Expansion", - "description": "Amount of offset applied to all polygons in each layer. Positive values can compensate for too big holes; negative values can compensate for too small holes.", - "unit": "mm", - "type": "float", - "min_value_warning": "-10", - "max_value_warning": "10", - "default": 0, - "visible": false - }, - "z_seam_type": { - "label": "Z Seam Alignment", - "description": "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these at the back, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker.", - "type": "enum", - "options": { - "back": "Back", - "shortest": "Shortest", - "random": "Random" - }, - "default": "shortest", - "visible": false - }, - "skin_no_small_gaps_heuristic": { - "label": "Ignore Small Z Gaps", - "description": "When the model has small vertical gaps, about 5% extra computation time can be spent on generating top and bottom skin in these narrow spaces. In such case, disable the setting.", - "type": "boolean", - "default": true, - "visible": false - } - } - }, - "infill": { - "label": "Infill", - "visible": true, - "icon": "category_infill", - "settings": { - "infill_sparse_density": { - "label": "Infill Density", - "description": "Adjusts the density of infill of the print.", - "unit": "%", - "type": "float", - "default": 20, - "min_value": "0", - "max_value_warning": "100", - "children": { - "infill_line_distance": { - "label": "Infill Line Distance", - "description": "Distance between the printed infill lines. This setting is calculated by the infill density and the infill line width.", - "unit": "mm", - "type": "float", - "default": 2, - "min_value": "0", - "visible": false, - "inherit_function": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density" - } - } - }, - "infill_pattern": { - "label": "Infill Pattern", - "description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle and concentric patterns are fully printed every layer.", - "type": "enum", - "visible": false, - "options": { - "grid": "Grid", - "lines": "Lines", - "triangles": "Triangles", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default": "grid", - "inherit_function": "'lines' if infill_sparse_density > 25 else 'grid'" - }, - "infill_overlap": { - "label": "Infill Overlap Percentage", - "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", - "unit": "%", - "type": "float", - "default": 10, - "inherit_function": "10 if infill_sparse_density < 95 else 0", - "min_value_warning": "-50", - "max_value_warning": "100", - "visible": false, - "children": { - "infill_overlap_mm": { - "label": "Infill Overlap", - "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", - "unit": "mm", - "type": "float", - "default": 0.04, - "min_value_warning": "-0.5 * machine_nozzle_size", - "max_value_warning": "machine_nozzle_size", - "inherit_function": "infill_line_width * parent_value / 100 if infill_sparse_density < 95 else 0", - "visible": false - } - } - }, - "infill_wipe_dist": { - "label": "Infill Wipe Distance", - "description": "Distance of a travel move inserted after every infill line, to make the infill stick to the walls better. This option is similar to infill overlap, but without extrusion and only on one end of the infill line.", - "unit": "mm", - "type": "float", - "default": 0.04, - "inherit_function": "wall_line_width_0 / 4 if wall_line_count == 1 else wall_line_width_x / 4", - "min_value_warning": "0", - "max_value_warning": "machine_nozzle_size", - "visible": false - }, - "infill_sparse_thickness": { - "label": "Infill Layer Thickness", - "description": "The thickness per layer of infill material. This value should always be a multiple of the layer height and is otherwise rounded.", - "unit": "mm", - "type": "float", - "default": 0.1, - "min_value": "0.0001", - "max_value_warning": "0.32", - "max_value": "layer_height * 8", - "visible": false, - "inherit_function": "layer_height" - }, - "infill_before_walls": { - "label": "Infill Before Walls", - "description": "Print the infill before printing the walls. Printing the walls first may lead to more accurate walls, but overhangs print worse. Printing the infill first leads to sturdier walls, but the infill pattern might sometimes show through the surface.", - "type": "boolean", - "default": true, - "visible": false - } - } - }, - "material": { - "label": "Material", - "visible": true, - "icon": "category_material", - "settings": { - "material_flow_dependent_temperature": { - "label": "Auto Temperature", - "description": "Change the temperature for each layer automatically with the average flow speed of that layer.", - "type": "boolean", - "default": false, - "visible": false, - "enabled": "False", - "global_only": true - }, - "material_print_temperature": { - "label": "Printing Temperature", - "description": "The temperature used for printing. Set at 0 to pre-heat the printer manually.", - "unit": "°C", - "type": "float", - "default": 210, - "min_value": "0", - "max_value_warning": "260", - "enabled": "not (material_flow_dependent_temperature)" - }, - "material_flow_temp_graph": { - "label": "Flow Temperature Graph", - "description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).", - "unit": "", - "type": "string", - "default": "[[3.5,200],[7.0,240]]", - "enabled": "False", - "enabled_before_removal": "material_flow_dependent_temperature", - "global_only": true - }, - "material_extrusion_cool_down_speed": { - "label": "Extrusion Cool Down Speed Modifier", - "description": "The extra speed by which the nozzle cools while extruding. The same value is used to signify the heat up speed lost when heating up while extruding.", - "unit": "°C/s", - "type": "float", - "default": 0.5, - "min_value": "0", - "max_value_warning": "10.0", - "global_only": "True", - "enabled": "False", - "enabled_before_removal": "material_flow_dependent_temperature or machine_extruder_count > 1", - "visible": false - }, - "material_bed_temperature": { - "label": "Bed Temperature", - "description": "The temperature used for the heated bed. Set at 0 to pre-heat the printer manually.", - "unit": "°C", - "type": "float", - "default": 60, - "min_value": "0", - "max_value_warning": "260", - "enabled": "machine_heated_bed", - "global_only": "True" - }, - "material_diameter": { - "label": "Diameter", - "description": "Adjusts the diameter of the filament used. Match this value with the diameter of the used filament.", - "unit": "mm", - "type": "float", - "default": 2.85, - "min_value": "0.0001", - "min_value_warning": "0.4", - "max_value_warning": "3.5", - "global_only": "True" - }, - "material_flow": { - "label": "Flow", - "description": "Flow compensation: the amount of material extruded is multiplied by this value.", - "unit": "%", - "default": 100, - "type": "float", - "min_value": "5", - "min_value_warning": "50", - "max_value_warning": "150" - }, - "retraction_enable": { - "label": "Enable Retraction", - "description": "Retract the filament when the nozzle is moving over a non-printed area. ", - "type": "boolean", - "default": true, - "visible": true - }, - "retraction_amount": { - "label": "Retraction Distance", - "description": "The length of material retracted during a retraction move.", - "unit": "mm", - "type": "float", - "default": 4.5, - "min_value_warning": "-0.0001", - "max_value_warning": "10.0", - "visible": false, - "inherit": false, - "enabled": "retraction_enable" - }, - "retraction_speed": { - "label": "Retraction Speed", - "description": "The speed at which the filament is retracted and primed during a retraction move.", - "unit": "mm/s", - "type": "float", - "default": 25, - "min_value": "0", - "max_value": "299792458000", - "max_value_warning": "100", - "visible": false, - "inherit": false, - "enabled": "retraction_enable", - "children": { - "retraction_retract_speed": { - "label": "Retraction Retract Speed", - "description": "The speed at which the filament is retracted during a retraction move.", - "unit": "mm/s", - "type": "float", - "default": 25, - "min_value": "0", - "max_value": "299792458000", - "max_value_warning": "100", - "visible": false, - "enabled": "retraction_enable" - }, - "retraction_prime_speed": { - "label": "Retraction Prime Speed", - "description": "The speed at which the filament is primed during a retraction move.", - "unit": "mm/s", - "type": "float", - "default": 25, - "min_value": "0", - "max_value": "299792458000", - "max_value_warning": "100", - "visible": false, - "enabled": "retraction_enable" - } - } - }, - "retraction_extra_prime_amount": { - "label": "Retraction Extra Prime Amount", - "description": "Some material can ooze away during a travel move, which can be compensated for here.", - "unit": "mm³", - "type": "float", - "default": 0, - "min_value_warning": "-0.0001", - "max_value_warning": "5.0", - "visible": false, - "inherit": false, - "enabled": "retraction_enable" - }, - "retraction_min_travel": { - "label": "Retraction Minimum Travel", - "description": "The minimum distance of travel needed for a retraction to happen at all. This helps to get fewer retractions in a small area.", - "unit": "mm", - "type": "float", - "default": 1.5, - "inherit_function": "line_width * 2", - "min_value": "0", - "max_value_warning": "10", - "visible": false, - "inherit": false, - "enabled": "retraction_enable" - }, - "retraction_count_max": { - "label": "Maximum Retraction Count", - "description": "This setting limits the number of retractions occurring within the minimum extrusion distance window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament, as that can flatten the filament and cause grinding issues.", - "default": 45, - "min_value": "0", - "max_value_warning": "100", - "type": "int", - "visible": false, - "inherit": false, - "enabled": "retraction_enable" - }, - "retraction_extrusion_window": { - "label": "Minimum Extrusion Distance Window", - "description": "The window in which the maximum retraction count is enforced. This value should be approximately the same as the retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", - "unit": "mm", - "type": "float", - "default": 4.5, - "min_value": "0", - "max_value_warning": "retraction_amount * 2", - "visible": false, - "inherit_function": "retraction_amount", - "enabled": "retraction_enable" - }, - "retraction_hop": { - "label": "Z Hop when Retracting", - "description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.", - "unit": "mm", - "type": "float", - "default": 0, - "min_value_warning": "-0.0001", - "max_value_warning": "10", - "visible": false, - "inherit": false, - "enabled": "retraction_enable" - } - } - }, - "speed": { - "label": "Speed", - "visible": true, - "icon": "category_speed", - "settings": { - "speed_print": { - "label": "Print Speed", - "description": "The speed at which printing happens.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value_warning": "150", - "max_value": "299792458000", - "default": 60, - "children": { - "speed_infill": { - "label": "Infill Speed", - "description": "The speed at which infill is printed.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 60, - "visible": false - }, - "speed_wall": { - "label": "Wall Speed", - "description": "The speed at which the walls are printed.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 30, - "visible": false, - "inherit_function": "parent_value / 2", - "children": { - "speed_wall_0": { - "label": "Outer Wall Speed", - "description": "The speed at which the outermost walls are printed. Printing the outer wall at a lower speed improves the final skin quality. However, having a large difference between the inner wall speed and the outer wall speed will effect quality in a negative way.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 30, - "visible": false - }, - "speed_wall_x": { - "label": "Inner Wall Speed", - "description": "The speed at which all inner walls are printed Printing the inner wall faster than the outer wall will reduce printing time. It works well to set this in between the outer wall speed and the infill speed.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 60, - "visible": false, - "inherit_function": "parent_value * 2" - } - } - }, - "speed_topbottom": { - "label": "Top/Bottom Speed", - "description": "The speed at which top/bottom layers are printed.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 30, - "visible": false, - "inherit_function": "parent_value / 2" - }, - "speed_support": { - "label": "Support Speed", - "description": "The speed at which the support structure is printed. Printing support at higher speeds can greatly reduce printing time. The surface quality of the support structure is not important since it is removed after printing.", - "unit": "mm/s", - "type": "float", - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "default": 60, - "visible": false, - "inherit_function": "speed_print", - "enabled": "support_enable", - "children": { - "speed_support_infill": { - "label": "Support Infill Speed", - "description": "The speed at which the infill of support is printed. Printing the infill at lower speeds improves stability.", - "unit": "mm/s", - "type": "float", - "default": 60, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "visible": false, - "inherit": true, - "enabled": "support_roof_enable", - "global_only": true - }, - "speed_support_roof": { - "label": "Support Roof Speed", - "description": "The speed at which the roofs of support are printed. Printing the support roof at lower speeds can improve overhang quality.", - "unit": "mm/s", - "type": "float", - "default": 40, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "visible": false, - "enabled": "support_roof_enable", - "inherit_function": "parent_value / 1.5", - "global_only": true - } - } - } - } - }, - "speed_travel": { - "label": "Travel Speed", - "description": "The speed at which travel moves are made.", - "unit": "mm/s", - "type": "float", - "default": 120, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "300", - "inherit_function": "speed_print if magic_spiralize else 120", - "global_only": true - }, - "speed_layer_0": { - "label": "Initial Layer Speed", - "description": "The print speed for the initial layer. A lower value is advised to improve adhesion to the build plate.", - "unit": "mm/s", - "type": "float", - "default": 30, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "300", - "visible": false - }, - "skirt_speed": { - "label": "Skirt Speed", - "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt at a different speed.", - "unit": "mm/s", - "type": "float", - "default": 30, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "300", - "visible": false, - "inherit_function": "speed_layer_0", - "global_only": true - }, - "speed_slowdown_layers": { - "label": "Number of Slower Layers", - "description": "The first few layers are printed slower than the rest of the object, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.", - "type": "int", - "default": 2, - "min_value": "0", - "max_value": "299792458000", - "max_value_warning": "300", - "visible": false, - "global_only": true - } - } - }, - "travel": { - "label": "Travel", - "visible": true, - "icon": "category_travel", - "settings": { - "retraction_combing": { - "label": "Enable Combing", - "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.", - "type": "enum", - "options": { - "off": "Off", - "all": "All", - "noskin": "No Skin" - }, - "default": true, - "visible": false, - "global_only": true - }, - "travel_avoid_other_parts": { - "label": "Avoid Printed Parts when Traveling", - "description": "The nozzle avoids already printed parts when traveling. This option is only available when combing is enabled.", - "type": "boolean", - "default": true, - "visible": false, - "enabled": "retraction_combing != \"off\"", - "global_only": "True" - }, - "travel_avoid_distance": { - "label": "Travel Avoid Distance", - "description": "The distance between the nozzle and already printed parts when avoiding during travel moves.", - "unit": "mm", - "type": "float", - "default": 1.5, - "inherit_function": "machine_nozzle_tip_outer_diameter / 2 * 1.25", - "min_value": "0", - "max_value_warning": "machine_nozzle_tip_outer_diameter * 5", - "visible": false, - "inherit": false, - "enabled": "retraction_combing != \"off\" and travel_avoid_other_parts", - "global_only": "True" - } - } - }, - "cooling": { - "label": "Cooling", - "visible": true, - "icon": "category_cool", - "settings": { - "cool_fan_enabled": { - "label": "Enable Cooling Fans", - "description": "Enables the cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.", - "type": "boolean", - "default": true, - "global_only": "True" - }, - "cool_fan_speed": { - "label": "Fan Speed", - "description": "The speed at which the cooling fans spin.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "default": 100, - "visible": false, - "inherit_function": "100.0 if cool_fan_enabled else 0.0", - "enabled": "cool_fan_enabled", - "global_only": "True", - "children": { - "cool_fan_speed_min": { - "label": "Regular Fan Speed", - "description": "The speed at which the fans spin before hitting the threshold. When a layer prints faster than the threshold, the fan speed gradually inclines towards the maximum fan speed.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "inherit_function": "parent_value", - "default": 100, - "visible": false, - "enabled": "cool_fan_enabled", - "global_only": "True" - }, - "cool_fan_speed_max": { - "label": "Maximum Fan Speed", - "description": "The speed at which the fans spin on the minimum layer time. The fan speed gradually increases between the regular fan speed and maximum fan speed when the threshold is hit.", - "unit": "%", - "type": "float", - "min_value": "max(0, cool_fan_speed_min)", - "max_value": "100", - "default": 100, - "visible": false, - "enabled": "cool_fan_enabled", - "global_only": "True" - } - } - }, - "cool_min_layer_time_fan_speed_max": { - "label": "Regular/Maximum Fan Speed Threshold", - "description": "The layer time which sets the threshold between regular fan speed and maximum fan speed. Layers that print slower than this time use regular fan speed. For faster layers the fan speed gradually increases towards the maximum fan speed.", - "unit": "sec", - "type": "float", - "default": 10, - "min_value": "cool_min_layer_time", - "max_value_warning": "600", - "visible": false, - "global_only": "True" - }, - "cool_fan_full_at_height": { - "label": "Regular Fan Speed at Height", - "description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from zero to regular fan speed.", - "unit": "mm", - "type": "float", - "default": 0.5, - "inherit_function": "layer_height_0", - "min_value": "0", - "max_value_warning": "10.0", - "visible": false, - "global_only": "True", - "children": { - "cool_fan_full_layer": { - "label": "Regular Fan Speed at Layer", - "description": "The layer at which the fans spin on regular fan speed. If regular fan speed at height is set, this value is calculated and rounded to a whole number.", - "type": "int", - "default": 1, - "min_value": "0", - "max_value_warning": "100", - "visible": false, - "inherit_function": "int((parent_value - layer_height_0 + 0.001) / layer_height) + 1", - "global_only": "True" - } - } - }, - "cool_min_layer_time": { - "label": "Minimum Layer Time", - "description": "The minimum time spent in a layer. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer.", - "unit": "sec", - "type": "float", - "default": 5, - "min_value": "0", - "max_value_warning": "600", - "visible": false, - "global_only": "True" - }, - "cool_min_speed": { - "label": "Minimum Speed", - "description": "The minimum print speed, despite slowing down due to the minimum layer time. When the printer would slow down too much, the pressure in the nozzle would be too low and result in bad print quality.", - "unit": "mm/s", - "type": "float", - "default": 10, - "min_value": "0", - "max_value_warning": "100", - "visible": false, - "global_only": "True" - }, - "cool_lift_head": { - "label": "Lift Head", - "description": "When the minimum speed is hit because of minimum layer time, lift the head away from the print and wait the extra time until the minimum layer time is reached.", - "type": "boolean", - "default": false, - "visible": false, - "global_only": "True" - } - } - }, - "support": { - "label": "Support", - "visible": true, - "icon": "category_support", - "settings": { - "support_enable": { - "label": "Enable Support", - "description": "Enable support structures. These structures support parts of the model with severe overhangs.", - "type": "boolean", - "default": false - }, - "support_type": { - "label": "Support Placement", - "description": "Adjusts the placement of the support structures. The placement can be set to touching build plate or everywhere. When set to everywhere the support structures will also be printed on the model.", - "type": "enum", - "options": { - "buildplate": "Touching Buildplate", - "everywhere": "Everywhere" - }, - "default": "everywhere", - "enabled": "support_enable" - }, - "support_angle": { - "label": "Support Overhang Angle", - "description": "The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.", - "unit": "°", - "type": "float", - "min_value": "0", - "max_value": "90", - "default": 50, - "visible": false, - "enabled": "support_enable" - }, - "support_pattern": { - "label": "Support Pattern", - "description": "The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.", - "type": "enum", - "options": { - "lines": "Lines", - "grid": "Grid", - "triangles": "Triangles", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default": "zigzag", - "visible": false, - "enabled": "support_enable", - "global_only": true - }, - "support_connect_zigzags": { - "label": "Connect Support ZigZags", - "description": "Connect the ZigZags. This will increase the strength of the zig zag support structure.", - "type": "boolean", - "default": true, - "visible": false, - "enabled": "support_enable and (support_pattern == \"zigzag\")", - "global_only": true - }, - "support_infill_rate": { - "label": "Support Density", - "description": "Adjusts the density of the support structure. A higher value results in better overhangs, but the supports are harder to remove.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value_warning": "100", - "default": 15, - "visible": false, - "enabled": "support_enable", - "global_only": true, - "children": { - "support_line_distance": { - "label": "Support Line Distance", - "description": "Distance between the printed support structure lines. This setting is calculated by the support density.", - "unit": "mm", - "type": "float", - "min_value": "0", - "default": 2.66, - "visible": false, - "enabled": "support_enable", - "inherit_function": "(support_line_width * 100) / parent_value", - "global_only": true - } - } - }, - "support_xy_distance": { - "label": "Support X/Y Distance", - "description": "Distance of the support structure from the print in the X/Y directions.", - "unit": "mm", - "type": "float", - "min_value": "0", - "max_value_warning": "10", - "default": 0.7, - "visible": false, - "enabled": "support_enable" - }, - "support_z_distance": { - "label": "Support Z Distance", - "description": "Distance from the top/bottom of the support structure to the print. This gap provides clearance to remove the supports after the model is printed. This value is rounded down to a multiple of the layer height.", - "unit": "mm", - "type": "float", - "min_value": "0", - "max_value_warning": "10", - "default": 0.15, - "visible": false, - "enabled": "support_enable", - - "children": { - "support_top_distance": { - "label": "Support Top Distance", - "description": "Distance from the top of the support to the print.", - "unit": "mm", - "min_value": "0", - "max_value_warning": "10", - "default": 0.15, - "type": "float", - "visible": false, - "enabled": "support_enable" - }, - "support_bottom_distance": { - "label": "Support Bottom Distance", - "description": "Distance from the print to the bottom of the support.", - "unit": "mm", - "min_value": "0", - "max_value_warning": "10", - "default": 0.1, - "inherit_function": "0.1 if support_type == 'everywhere' else 0", - "type": "float", - "visible": false, - "enabled": "support_enable and support_type == 'everywhere'" - } - } - }, - "support_bottom_stair_step_height": { - "label": "Support Stair Step Height", - "description": "The height of the steps of the stair-like bottom of support resting on the model. A low value makes the support harder to remove, but too high values can lead to unstable support structures.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": "0", - "max_value_warning": "1.0", - "visible": false, - "enabled": "support_enable" - }, - "support_join_distance": { - "label": "Support Join Distance", - "description": "The maximum distance between support structures in the X/Y directions. When seperate structures are closer together than this value, the structures merge into one.", - "unit": "mm", - "type": "float", - "default": 2.0, - "min_value_warning": "0", - "max_value_warning": "10", - "visible": false, - "enabled": "support_enable" - }, - "support_offset": { - "label": "Support Horizontal Expansion", - "description": "Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.", - "unit": "mm", - "type": "float", - "default": 0.2, - "min_value_warning": "-0.5", - "max_value_warning": "5.0", - "visible": false, - "enabled": "support_enable" - }, - "support_area_smoothing": { - "label": "Support Area Smoothing", - "description": "Maximum distance in the X/Y directions of a line segment which is to be smoothed out. Ragged lines are introduced by the join distance and support bridge, which cause the machine to resonate. Smoothing the support areas won't cause them to break with the constraints, except it might change the overhang.", - "unit": "mm", - "type": "float", - "default": 0.6, - "min_value": "0", - "max_value_warning": "1.0", - "visible": false, - "enabled": "support_enable" - }, - "support_roof_enable": { - "label": "Enable Support Roof", - "description": "Generate a dense top skin at the top of the support on which the model is printed.", - "type": "boolean", - "default": false, - "visible": true, - "enabled": "support_enable" - }, - "support_roof_height": { - "label": "Support Roof Thickness", - "description": "The thickness of the support roofs.", - "unit": "mm", - "type": "float", - "default": 1, - "min_value": "0", - "max_value_warning": "10", - "visible": false, - "enabled": "support_roof_enable" - }, - "support_roof_density": { - "label": "Support Roof Density", - "description": "Adjusts the density of the roof of the support structure. A higher value results in better overhangs, but the supports are harder to remove.", - "unit": "%", - "type": "float", - "default": 100, - "min_value": "0", - "max_value_warning": "100", - "enabled":"support_roof_enable", - "global_only": true, - "children": { - "support_roof_line_distance": { - "label": "Support Roof Line Distance", - "description": "Distance between the printed support roof lines. This setting is calculated by the support roof Density, but can be adjusted separately.", - "unit": "mm", - "type": "float", - "default": 0.4, - "min_value": "0", - "visible": false, - "inherit_function": "0 if parent_value == 0 else (support_roof_line_width * 100) / parent_value", - "enabled": "support_roof_enable", - "global_only": true - } - } - }, - "support_roof_pattern": { - "label": "Support Roof Pattern", - "description": "The pattern with which the top of the support is printed.", - "type": "enum", - "visible": false, - "options": { - "lines": "Lines", - "grid": "Grid", - "triangles": "Triangles", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default": "concentric", - "enabled": "support_roof_enable", - "global_only": true - }, - "support_use_towers": { - "label": "Use Towers", - "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", - "type": "boolean", - "default": true, - "visible": false, - "enabled": "support_enable" - }, - "support_tower_diameter": { - "label": "Tower Diameter", - "description": "The diameter of a special tower.", - "unit": "mm", - "type": "float", - "default": 3.0, - "min_value": "0", - "max_value_warning": "10", - "visible": false, - "enabled": "support_enable and support_use_towers" - }, - "support_minimal_diameter": { - "label": "Minimum Diameter", - "description": "Minimum diameter in the X/Y directions of a small area which is to be supported by a specialized support tower.", - "unit": "mm", - "type": "float", - "default": 3.0, - "min_value": "0", - "max_value_warning": "10", - "max_value": "support_tower_diameter", - "inherit": true, - "visible": false, - "enabled": "support_enable and support_use_towers" - }, - "support_tower_roof_angle": { - "label": "Tower Roof Angle", - "description": "The angle of a rooftop of a tower. A higher value results in pointed tower roofs, a lower value results in flattened tower roofs.", - "unit": "°", - "type": "int", - "min_value": "0", - "max_value": "90", - "default": 65, - "visible": false, - "enabled": "support_enable and support_use_towers" - } - } - }, - "platform_adhesion": { - "label": "Platform Adhesion", - "visible": true, - "icon": "category_adhesion", - "settings": { - "adhesion_type": { - "label": "Platform Adhesion Type", - "description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your object to prevent warping. Raft adds a thick grid with a roof below the object. Skirt is a line printed around the object, but not connected to the model.", - "type": "enum", - "options": { - "skirt": "Skirt", - "brim": "Brim", - "raft": "Raft" - }, - "default": "brim", - "global_only": "True" - }, - "skirt_line_count": { - "label": "Skirt Line Count", - "description": "Multiple skirt lines help to prime your extrusion better for small objects. Setting this to 0 will disable the skirt.", - "type": "int", - "default": 1, - "min_value": "0", - "max_value_warning": "10", - "enabled": "adhesion_type == \"skirt\"", - "global_only": "True", - "visible": false - }, - "skirt_gap": { - "label": "Skirt Distance", - "description": "The horizontal distance between the skirt and the first layer of the print.\nThis is the minimum distance, multiple skirt lines will extend outwards from this distance.", - "unit": "mm", - "type": "float", - "default": 3, - "min_value_warning": "0", - "max_value_warning": "100", - "enabled": "adhesion_type == \"skirt\"", - "global_only": "True", - "visible": false - }, - "skirt_minimal_length": { - "label": "Skirt Minimum Length", - "description": "The minimum length of the skirt. If this length is not reached by the skirt line count, more skirt lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", - "unit": "mm", - "type": "float", - "default": 250, - "min_value": "0", - "min_value_warning": "25", - "max_value_warning": "2500", - "enabled": "adhesion_type == \"skirt\"", - "global_only": "True", - "visible": false - }, - "brim_width": { - "label": "Brim Width", - "description": "The distance from the model to the outermost brim line. A larger brim enhances adhesion to the build plate, but also reduces the effective print area.", - "type": "float", - "unit": "mm", - "default": 8.0, - "min_value": "0.0", - "max_value_warning": "100.0", - "enabled": "adhesion_type == \"brim\"", - "global_only": "True", - "visible": true, - "children": { - "brim_line_count": { - "label": "Brim Line Count", - "description": "The number of lines used for a brim. More brim lines enhance adhesion to the build plate, but also reduces the effective print area.", - "type": "int", - "default": 20, - "min_value": "0", - "max_value_warning": "300", - "inherit_function": "math.ceil(parent_value / skirt_line_width)", - "enabled": "adhesion_type == \"brim\"", - "global_only": "True", - "visible": false - } - } - }, - "raft_margin": { - "label": "Raft Extra Margin", - "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", - "unit": "mm", - "type": "float", - "default": 5, - "min_value_warning": "0", - "max_value_warning": "10", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_airgap": { - "label": "Raft Air Gap", - "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", - "unit": "mm", - "type": "float", - "default": 0.35, - "min_value": "0", - "max_value_warning": "1.0", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": true - }, - "raft_surface_layers": { - "label": "Raft Top Layers", - "description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers result in a smoother top surface than 1.", - "type": "int", - "default": 2, - "min_value": "0", - "max_value_warning": "20", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": true - }, - "raft_surface_thickness": { - "label": "Raft Top Layer Thickness", - "description": "Layer thickness of the top raft layers.", - "unit": "mm", - "type": "float", - "default": 0.1, - "min_value": "0", - "max_value_warning": "2.0", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_surface_line_width": { - "label": "Raft Top Line Width", - "description": "Width of the lines in the top surface of the raft. These can be thin lines so that the top of the raft becomes smooth.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": "0.0001", - "max_value_warning": "machine_nozzle_size * 2", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_surface_line_spacing": { - "label": "Raft Top Spacing", - "description": "The distance between the raft lines for the top raft layers. The spacing should be equal to the line width, so that the surface is solid.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": "0.0001", - "max_value_warning": "5.0", - "enabled": "adhesion_type == \"raft\"", - "inherit_function": "raft_surface_line_width", - "global_only": "True", - "visible": false - }, - "raft_interface_thickness": { - "label": "Raft Middle Thickness", - "description": "Layer thickness of the middle raft layer.", - "unit": "mm", - "type": "float", - "default": 0.27, - "min_value": "0", - "max_value_warning": "5.0", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_interface_line_width": { - "label": "Raft Middle Line Width", - "description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.", - "unit": "mm", - "type": "float", - "default": 1, - "min_value": "0.0001", - "max_value_warning": "machine_nozzle_size * 2", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_interface_line_spacing": { - "label": "Raft Middle Spacing", - "description": "The distance between the raft lines for the middle raft layer. The spacing of the middle should be quite wide, while being dense enough to support the top raft layers.", - "unit": "mm", - "type": "float", - "default": 1.0, - "min_value": "0", - "max_value_warning": "15.0", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_base_thickness": { - "label": "Raft Base Thickness", - "description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer bed.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": "0", - "max_value_warning": "5.0", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_base_line_width": { - "label": "Raft Base Line Width", - "description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.", - "unit": "mm", - "type": "float", - "default": 1, - "min_value": "0.0001", - "max_value_warning": "machine_nozzle_size * 2", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_base_line_spacing": { - "label": "Raft Line Spacing", - "description": "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate.", - "unit": "mm", - "type": "float", - "default": 3.0, - "min_value": "0.0001", - "max_value_warning": "100", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True", - "visible": false - }, - "raft_speed": { - "label": "Raft Print Speed", - "description": "The speed at which the raft is printed.", - "unit": "mm/s", - "type": "float", - "default": 30, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "200", - "enabled": "adhesion_type == \"raft\"", - "inherit_function": "speed_print / 60 * 30", - "global_only": "True", - "visible": false, - "children": { - "raft_surface_speed": { - "label": "Raft Surface Print Speed", - "description": "The speed at which the surface raft layers are printed. These should be printed a bit slower, so that the nozzle can slowly smooth out adjacent surface lines.", - "unit": "mm/s", - "type": "float", - "default": 30, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "100", - "enabled": "adhesion_type == \"raft\"", - "inherit_function": "parent_value", - "global_only": "True", - "visible": false - }, - "raft_interface_speed": { - "label": "Raft Interface Print Speed", - "description": "The speed at which the interface raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.", - "unit": "mm/s", - "type": "float", - "default": 15, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "150", - "enabled": "adhesion_type == \"raft\"", - "inherit_function": "0.5 * parent_value", - "global_only": "True", - "visible": false - }, - "raft_base_speed": { - "label": "Raft Base Print Speed", - "description": "The speed at which the base raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.", - "unit": "mm/s", - "type": "float", - "default": 15, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "200", - "enabled": "adhesion_type == \"raft\"", - "inherit_function": "0.5 * parent_value", - "global_only": "True", - "visible": false - } - } - }, - "raft_fan_speed": { - "label": "Raft Fan Speed", - "description": "The fan speed for the raft.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "default": 100, - "global_only": "True", - "visible": false, - "enabled": "adhesion_type == \"raft\"", - "children": { - "raft_surface_fan_speed": { - "label": "Raft Surface Fan Speed", - "description": "The fan speed for the surface raft layers.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "default": 100, - "global_only": "True", - "visible": false, - "inherit": true, - "enabled": "adhesion_type == \"raft\"" - }, - "raft_interface_fan_speed": { - "label": "Raft Interface Fan Speed", - "description": "The fan speed for the interface raft layer.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "default": 100, - "global_only": "True", - "visible": false, - "inherit": true, - "enabled": "adhesion_type == \"raft\"" - }, - "raft_base_fan_speed": { - "label": "Raft Base Fan Speed", - "description": "The fan speed for the base raft layer.", - "unit": "%", - "type": "float", - "min_value": "0", - "max_value": "100", - "default": 100, - "global_only": "True", - "visible": false, - "inherit": true, - "enabled": "adhesion_type == \"raft\"" - } - } - } - } - }, - "meshfix": { - "label": "Mesh Fixes", - "visible": true, - "icon": "category_fixes", - "settings": { - "meshfix_union_all": { - "label": "Union Overlapping Volumes", - "description": "Ignore the internal geometry arising from overlapping volumes and print the volumes as one. This may cause internal cavities to disappear.", - "type": "boolean", - "default": true, - "visible": false - }, - "meshfix_union_all_remove_holes": { - "label": "Remove All Holes", - "description": "Remove the holes in each layer and keep only the outside shape. This will ignore any invisible internal geometry. However, it also ignores layer holes which can be viewed from above or below.", - "type": "boolean", - "default": false, - "visible": false - }, - "meshfix_extensive_stitching": { - "label": "Extensive Stitching", - "description": "Extensive stitching tries to stitch up open holes in the mesh by closing the hole with touching polygons. This option can introduce a lot of processing time.", - "type": "boolean", - "default": false, - "visible": false - }, - "meshfix_keep_open_polygons": { - "label": "Keep Disconnected Faces", - "description": "Normally Cura tries to stitch up small holes in the mesh and remove parts of a layer with big holes. Enabling this option keeps those parts which cannot be stitched. This option should be used as a last resort option when everything else fails to produce proper GCode.", - "type": "boolean", - "default": false, - "visible": false - } - } - }, - "blackmagic": { - "label": "Special Modes", - "visible": true, - "icon": "category_blackmagic", - "settings": { - "print_sequence": { - "label": "Print Sequence", - "description": "Whether to print all objects one layer at a time or to wait for one object to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.", - "type": "enum", - "options": { - "all_at_once": "All at Once", - "one_at_a_time": "One at a Time" - }, - "default": "all_at_once", - "visible": true, - "global_only": true - }, - "magic_mesh_surface_mode": { - "label": "Surface Mode", - "description": "Treat the model as a surface only, a volume, or volumes with loose surfaces. The normal print mode only prints enclosed volumes. \"Surface\" prints a single wall tracing the mesh surface with no infill and no top/bottom skin. \"Both\" prints enclosed volumes like normal and any remaining polygons as surfaces.", - "type": "enum", - "options": { - "normal": "Normal", - "surface": "Surface", - "both": "Both" - }, - "default": "normal", - "visible": false - }, - "magic_spiralize": { - "label": "Spiralize Outer Contour", - "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid object into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", - "type": "boolean", - "default": false, - "visible": false, - "global_only": "True" - } - } - }, - "experimental": - { - "label": "Experimental", - "visible": true, - "icon": "category_blackmagic", - "settings": { - "draft_shield_enabled": { - "label": "Enable Draft Shield", - "description": "This will create a wall around the object, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.", - "type": "boolean", - "default": false, - "global_only": true - }, - "draft_shield_dist": { - "label": "Draft Shield X/Y Distance", - "description": "Distance of the draft shield from the print, in the X/Y directions.", - "unit": "mm", - "type": "float", - "min_value": "0", - "max_value_warning": "100", - "default": 10, - "visible": false, - "enabled": "draft_shield_enabled", - "global_only": true - }, - "draft_shield_height_limitation": { - "label": "Draft Shield Limitation", - "description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the object or at a limited height.", - "type": "enum", - "options": { - "full": "Full", - "limited": "Limited" - }, - "default": "full", - "visible": false, - "enabled": "draft_shield_enabled", - "global_only": true - }, - "draft_shield_height": { - "label": "Draft Shield Height", - "description": "Height limitation of the draft shield. Above this height no draft shield will be printed.", - "unit": "mm", - "type": "float", - "min_value": "0", - "max_value_warning": "9999", - "default": 0, - "inherit_function": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0", - "visible": false, - "enabled": "draft_shield_height_limitation == \"limited\"", - "global_only": true - }, - "coasting_enable": { - "label": "Enable Coasting", - "description": "Coasting replaces the last part of an extrusion path with a travel path. The oozed material is used to print the last piece of the extrusion path in order to reduce stringing.", - "type": "boolean", - "default": false, - "visible": false, - "global_only": true - }, - "coasting_volume": { - "label": "Coasting Volume", - "description": "The volume otherwise oozed. This value should generally be close to the nozzle diameter cubed.", - "unit": "mm³", - "type": "float", - "default": 0.064, - "min_value": "0", - "max_value_warning": "2.0", - "visible": false, - "inherit": false, - "enabled": "coasting_enable", - "global_only": true - }, - "coasting_min_volume": { - "label": "Minimum Volume Before Coasting", - "description": "The smallest volume an extrusion path should have before allowing coasting. For smaller extrusion paths, less pressure has been built up in the bowden tube and so the coasted volume is scaled linearly. This value should always be larger than the Coasting Volume.", - "unit": "mm³", - "type": "float", - "default": 0.8, - "min_value": "0", - "max_value_warning": "10.0", - "visible": false, - "enabled": "coasting_enable", - "global_only": true - }, - "coasting_speed": { - "label": "Coasting Speed", - "description": "The speed by which to move during coasting, relative to the speed of the extrusion path. A value slightly under 100% is advised, since during the coasting move the pressure in the bowden tube drops.", - "unit": "%", - "type": "float", - "default": 90, - "min_value": "0.0001", - "max_value_warning": "100", - "visible": false, - "inherit": false, - "enabled": "coasting_enable", - "global_only": true - }, - "skin_outline_count": { - "label": "Extra Skin Wall Count", - "description": "Replaces the outermost part of the top/bottom pattern with a number of concentric lines. Using one or two lines improves roofs that start on infill material.", - "default": 0, - "min_value": "0", - "max_value_warning": "10", - "type": "int", - "visible": false - }, - "skin_alternate_rotation": { - "label": "Alternate Skin Rotation", - "description": "Alternate the direction in which the top/bottom layers are printed. Normally they are printed diagonally only. This setting adds the X-only and Y-only directions.", - "type": "boolean", - "default": false, - "visible": false, - "enabled": "top_bottom_pattern != \"concentric\"" - }, - "support_conical_enabled": { - "label": "Enable Conical Support", - "description": "Experimental feature: Make support areas smaller at the bottom than at the overhang.", - "type": "boolean", - "default": false, - "visible": true, - "enabled": "support_enable" - }, - "support_conical_angle": { - "label": "Conical Support Angle", - "description": "The angle of the tilt of conical support. With 0 degrees being vertical, and 90 degrees being horizontal. Smaller angles cause the support to be more sturdy, but consist of more material. Negative angles cause the base of the support to be wider than the top.", - "unit": "°", - "type": "float", - "min_value": "-90", - "min_value_warning": "-45", - "max_value_warning": "45", - "max_value": "90", - "default": 30, - "visible": false, - "enabled": "support_conical_enabled and support_enable" - }, - "support_conical_min_width": { - "label": "Conical Support Minimum Width", - "description": "Minimum width to which the base of the conical support area is reduced. Small widths can lead to unstable support structures.", - "unit": "mm", - "default": 5.0, - "min_value": "0", - "min_value_warning": "machine_nozzle_size * 3", - "max_value_warning": "100.0", - "type": "float", - "visible": false, - "enabled": "support_conical_enabled and support_enable" - }, - "magic_fuzzy_skin_enabled": { - "label": "Fuzzy Skin", - "description": "Randomly jitter while printing the outer wall, so that the surface has a rough and fuzzy look.", - "type": "boolean", - "default": false, - "visible": false - }, - "magic_fuzzy_skin_thickness": { - "label": "Fuzzy Skin Thickness", - "description": "The width within which to jitter. It's advised to keep this below the outer wall width, since the inner walls are unaltered.", - "type": "float", - "unit": "mm", - "default": 0.3, - "min_value": "0.001", - "max_value_warning": "wall_line_width_0", - "visible": false, - "enabled": "magic_fuzzy_skin_enabled" - }, - "magic_fuzzy_skin_point_density": { - "label": "Fuzzy Skin Density", - "description": "The average density of points introduced on each polygon in a layer. Note that the original points of the polygon are discarded, so a low density results in a reduction of the resolution.", - "type": "float", - "unit": "1/mm", - "default": 1.25, - "min_value": "0.008", - "min_value_warning": "0.1", - "max_value_warning": "10", - "max_value": "2 / magic_fuzzy_skin_thickness", - "visible": false, - "enabled": "magic_fuzzy_skin_enabled", - "children": { - "magic_fuzzy_skin_point_dist": { - "label": "Fuzzy Skin Point Distance", - "description": "The average distance between the random points introduced on each line segment. Note that the original points of the polygon are discarded, so a high smoothness results in a reduction of the resolution. This value must be higher than half the Fuzzy Skin Thickness.", - "type": "float", - "unit": "mm", - "default": 0.8, - "min_value": "magic_fuzzy_skin_thickness / 2", - "min_value_warning": "0.1", - "max_value_warning": "10", - "inherit_function": "10000 if parent_value == 0 else 1 / parent_value", - "visible": false, - "enabled": "magic_fuzzy_skin_enabled" - } - } - }, - "wireframe_enabled": { - "label": "Wire Printing", - "description": "Print only the outside surface with a sparse webbed structure, printing 'in thin air'. This is realized by horizontally printing the contours of the model at given Z intervals which are connected via upward and diagonally downward lines.", - "type": "boolean", - "default": false, - "visible": false, - "global_only": "True" - }, - "wireframe_height": { - "label": "WP Connection Height", - "description": "The height of the upward and diagonally downward lines between two horizontal parts. This determines the overall density of the net structure. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 3, - "min_value": "0.0001", - "max_value_warning": "20", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_roof_inset": { - "label": "WP Roof Inset Distance", - "description": "The distance covered when making a connection from a roof outline inward. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 3, - "min_value": "0", - "min_value_warning": "machine_nozzle_size", - "max_value_warning": "20", - "visible": false, - "enabled": "wireframe_enabled", - "inherit_function": "wireframe_height", - "global_only": "True" - }, - "wireframe_printspeed": { - "label": "WP Speed", - "description": "Speed at which the nozzle moves when extruding material. Only applies to Wire Printing.", - "unit": "mm/s", - "type": "float", - "default": 5, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "50", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True", - "children": { - "wireframe_printspeed_bottom": { - "label": "WP Bottom Printing Speed", - "description": "Speed of printing the first layer, which is the only layer touching the build platform. Only applies to Wire Printing.", - "unit": "mm/s", - "type": "float", - "default": 5, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "50", - "visible": false, - "inherit": true, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_printspeed_up": { - "label": "WP Upward Printing Speed", - "description": "Speed of printing a line upward 'in thin air'. Only applies to Wire Printing.", - "unit": "mm/s", - "type": "float", - "default": 5, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "50", - "visible": false, - "inherit": true, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_printspeed_down": { - "label": "WP Downward Printing Speed", - "description": "Speed of printing a line diagonally downward. Only applies to Wire Printing.", - "unit": "mm/s", - "type": "float", - "default": 5, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "50", - "visible": false, - "inherit": true, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_printspeed_flat": { - "label": "WP Horizontal Printing Speed", - "description": "Speed of printing the horizontal contours of the object. Only applies to Wire Printing.", - "unit": "mm/s", - "type": "float", - "default": 5, - "min_value": "0.1", - "max_value": "299792458000", - "max_value_warning": "100", - "visible": false, - "inherit": true, - "enabled": "wireframe_enabled", - "global_only": "True" - } - } - }, - "wireframe_flow": { - "label": "WP Flow", - "description": "Flow compensation: the amount of material extruded is multiplied by this value. Only applies to Wire Printing.", - "unit": "%", - "default": 100, - "min_value": "0", - "max_value_warning": "100", - "type": "float", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True", - "children": { - "wireframe_flow_connection": { - "label": "WP Connection Flow", - "description": "Flow compensation when going up or down. Only applies to Wire Printing.", - "unit": "%", - "default": 100, - "min_value": "0", - "max_value_warning": "100", - "type": "float", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_flow_flat": { - "label": "WP Flat Flow", - "description": "Flow compensation when printing flat lines. Only applies to Wire Printing.", - "unit": "%", - "default": 100, - "min_value": "0", - "max_value_warning": "100", - "type": "float", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - } - } - }, - "wireframe_top_delay": { - "label": "WP Top Delay", - "description": "Delay time after an upward move, so that the upward line can harden. Only applies to Wire Printing.", - "unit": "sec", - "type": "float", - "default": 0, - "min_value": "0", - "max_value_warning": "1", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_bottom_delay": { - "label": "WP Bottom Delay", - "description": "Delay time after a downward move. Only applies to Wire Printing.", - "unit": "sec", - "type": "float", - "default": 0, - "min_value": "0", - "max_value_warning": "1", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_flat_delay": { - "label": "WP Flat Delay", - "description": "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too long delays cause sagging. Only applies to Wire Printing.", - "unit": "sec", - "type": "float", - "default": 0.1, - "min_value": "0", - "max_value_warning": "0.5", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_up_half_speed": { - "label": "WP Ease Upward", - "description": "Distance of an upward move which is extruded with half speed.\nThis can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 0.3, - "min_value": "0", - "max_value_warning": "5.0", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_top_jump": { - "label": "WP Knot Size", - "description": "Creates a small knot at the top of an upward line, so that the consecutive horizontal layer has a better chance to connect to it. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 0.6, - "min_value": "0", - "max_value_warning": "2.0", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_fall_down": { - "label": "WP Fall Down", - "description": "Distance with which the material falls down after an upward extrusion. This distance is compensated for. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 0.5, - "min_value": "0", - "max_value_warning": "wireframe_height", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_drag_along": { - "label": "WP Drag Along", - "description": "Distance with which the material of an upward extrusion is dragged along with the diagonally downward extrusion. This distance is compensated for. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 0.6, - "min_value": "0", - "max_value_warning": "wireframe_height", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_strategy": { - "label": "WP Strategy", - "description": "Strategy for making sure two consecutive layers connect at each connection point. Retraction lets the upward lines harden in the right position, but may cause filament grinding. A knot can be made at the end of an upward line to heighten the chance of connecting to it and to let the line cool; however, it may require slow printing speeds. Another strategy is to compensate for the sagging of the top of an upward line; however, the lines won't always fall down as predicted.", - "type": "enum", - "options": { - "compensate": "Compensate", - "knot": "Knot", - "retract": "Retract" - }, - "default": "compensate", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_straight_before_down": { - "label": "WP Straighten Downward Lines", - "description": "Percentage of a diagonally downward line which is covered by a horizontal line piece. This can prevent sagging of the top most point of upward lines. Only applies to Wire Printing.", - "type": "float", - "unit": "%", - "default": 20, - "min_value": "0", - "max_value": "100", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_roof_fall_down": { - "label": "WP Roof Fall Down", - "description": "The distance which horizontal roof lines printed 'in thin air' fall down when being printed. This distance is compensated for. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 2, - "min_value_warning": "0", - "max_value_warning": "wireframe_roof_inset", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_roof_drag_along": { - "label": "WP Roof Drag Along", - "description": "The distance of the end piece of an inward line which gets dragged along when going back to the outer outline of the roof. This distance is compensated for. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 0.8, - "min_value": "0", - "max_value_warning": "10", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_roof_outer_delay": { - "label": "WP Roof Outer Delay", - "description": "Time spent at the outer perimeters of hole which is to become a roof. Longer times can ensure a better connection. Only applies to Wire Printing.", - "type": "float", - "unit": "sec", - "default": 0.2, - "min_value": "0", - "max_value_warning": "2.0", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - }, - "wireframe_nozzle_clearance": { - "label": "WP Nozzle Clearance", - "description": "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 less upward connections with the next layer. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 1, - "min_value_warning": "0", - "max_value_warning": "10.0", - "visible": false, - "enabled": "wireframe_enabled", - "global_only": "True" - } - } - } - } -} diff --git a/resources/machines/grr_neo.json b/resources/machines/grr_neo.json deleted file mode 100644 index e2eb9aae73..0000000000 --- a/resources/machines/grr_neo.json +++ /dev/null @@ -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 } - } -} diff --git a/resources/machines/innovo-inventor.json b/resources/machines/innovo-inventor.json deleted file mode 100644 index 7ba8b9a48c..0000000000 --- a/resources/machines/innovo-inventor.json +++ /dev/null @@ -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 } - } -} \ No newline at end of file diff --git a/resources/machines/m180.json b/resources/machines/m180.json deleted file mode 100644 index 7e31577ac2..0000000000 --- a/resources/machines/m180.json +++ /dev/null @@ -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" - } - } -} diff --git a/resources/machines/maker_starter.json b/resources/machines/maker_starter.json deleted file mode 100644 index fa0613ac1b..0000000000 --- a/resources/machines/maker_starter.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "id": "maker_starter", - "version": 1, - "name": "3DMaker Starter", - "manufacturer": "Other", - "author": "Other", - "icon": "icon_ultimaker2.png", - "platform": "makerstarter_platform.stl", - "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj", - "inherits": "fdmprinter.json", - - "overrides": { - "machine_width": { "default": 210 }, - "machine_depth": { "default": 185 }, - "machine_height": { "default": 200 }, - "machine_heated_bed": { "default": false }, - - "machine_center_is_zero": { "default": false }, - "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": 55 }, - "machine_gcode_flavor": { "default": "RepRap" }, - "machine_disallowed_areas": { "default": []}, - "machine_platform_offset": { "default": [0.0, 0.0, 0.0] }, - - "machine_nozzle_tip_outer_diameter": { "default": 1.0 }, - "machine_nozzle_head_distance": { "default": 3.0 }, - "machine_nozzle_expansion_angle": { "default": 45 }, - - "layer_height": { "default": 0.2 }, - "layer_height_0": { "default": 0.2, "visible": false }, - "wall_line_count": { "default": 2, "visible": true }, - "top_layers": { "default": 4, "visible": true }, - "bottom_layers": { "default": 4, "visible": true }, - "material_print_temperature": { "visible": false }, - "material_bed_temperature": { "visible": false }, - "material_diameter": { "default": 1.75, "visible": false }, - "material_flow": { "visible": false }, - "retraction_hop": { "default": 0.2 }, - "speed_print": { "default": 50.0 }, - "speed_wall": { "default": 30.0 }, - "speed_wall_0": { "default": 30.0 }, - "speed_wall_x": { "default": 30.0 }, - "speed_topbottom": { "default": 50.0 }, - "speed_support": { "default": 50.0 }, - "speed_travel": { "default": 120.0 }, - "speed_layer_0": { "default": 20.0 }, - "skirt_speed": { "default": 15.0 }, - "speed_slowdown_layers": { "default": 4 }, - "infill_sparse_density": { "default": 20.0 }, - "cool_fan_speed_min": { "default": 50.0 }, - "cool_fan_speed_max": { "default": 100.0 }, - "cool_fan_full_layer": { "default": 4, "visible": true }, - "cool_min_layer_time": { "default": 5.0 }, - "cool_min_layer_time_fan_speed_max": { "default": 10.0 }, - "support_type": { "default": "Everywhere" }, - "support_angle": { "default": 45.0, "visible": true }, - "support_xy_distance": { "default": 1 }, - "support_z_distance": { "default": 0.2 }, - "support_top_distance": { "default": 0.2 }, - "support_bottom_distance": { "default": 0.24 }, - "support_pattern": { "default": "ZigZag" }, - "support_infill_rate": { "default": 15, "visible": true }, - "adhesion_type": { "default": "Raft" }, - "skirt_minimal_length": { "default": 100.0 }, - "raft_base_line_spacing": { "default": 2.0 }, - "raft_base_thickness": { "default": 0.3 }, - "raft_base_line_width": { "default": 2.0 }, - "raft_base_speed": { "default": 15.0 }, - "raft_interface_thickness": { "default": 0.24 }, - "raft_interface_line_width": { "default": 0.6 }, - "raft_airgap": { "default": 0.2 }, - "raft_surface_layers": { "default": 2 } - } -} - - diff --git a/resources/machines/prusa_i3.json b/resources/machines/prusa_i3.json deleted file mode 100644 index dcbca32801..0000000000 --- a/resources/machines/prusa_i3.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "id": "prusa_i3", - "version": 1, - "name": "Prusa i3", - "manufacturer": "Other", - "author": "Other", - "icon": "icon_ultimaker2.png", - "platform": "prusai3_platform.stl", - "file_formats": "text/x-gcode", - "inherits": "fdmprinter.json", - - "overrides": { - "machine_heated_bed": { "default": true }, - "machine_width": { "default": 200 }, - "machine_height": { "default": 200 }, - "machine_depth": { "default": 200 }, - "machine_center_is_zero": { "default": false }, - "machine_nozzle_size": { "default": 0.4 }, - "material_diameter": { "default": 1.75 }, - "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" - } - } -} diff --git a/resources/machines/prusa_i3_xl.json b/resources/machines/prusa_i3_xl.json deleted file mode 100644 index b66b974983..0000000000 --- a/resources/machines/prusa_i3_xl.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "id": "prusa_i3_xl", - "version": 1, - "name": "Prusa i3 xl", - "manufacturer": "Other", - "author": "Other", - "icon": "icon_ultimaker2.png", - "platform": "prusai3_xl_platform.stl", - "file_formats": "text/x-gcode", - "inherits": "fdmprinter.json", - - "overrides": { - "machine_heated_bed": { "default": true }, - "machine_width": { "default": 200 }, - "machine_height": { "default": 200 }, - "machine_depth": { "default": 270 }, - "machine_center_is_zero": { "default": false }, - "machine_nozzle_size": { "default": 0.4 }, - "material_diameter": { "default": 1.75 }, - "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" - } - } -} diff --git a/resources/machines/ultimaker.json b/resources/machines/ultimaker.json deleted file mode 100644 index a7a9cd3994..0000000000 --- a/resources/machines/ultimaker.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "id": "ultimaker_base", - "version": 1, - "visible": false, - "name": "Ultimaker", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "inherits": "fdmprinter.json", - - "machine_preferences": { - "prefered_profile": "Normal Quality", - "prefered_variant": "0.4 mm", - "prefered_material": "PLA" - } -} diff --git a/resources/machines/ultimaker2.json b/resources/machines/ultimaker2.json deleted file mode 100644 index e19aec1336..0000000000 --- a/resources/machines/ultimaker2.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "id": "ultimaker2", - "version": 1, - "name": "Ultimaker 2", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "icon": "icon_ultimaker2.png", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "file_formats": "text/x-gcode", - - "inherits": "ultimaker.json", - - "machine_extruder_trains": { - "0": { - "machine_nozzle_heat_up_speed": { - "default": 2.0 - }, - "machine_nozzle_cool_down_speed": { - "default": 2.0 - }, - "machine_nozzle_tip_outer_diameter": { - "default": 1 - }, - "machine_nozzle_head_distance": { - "default": 3 - }, - "machine_nozzle_expansion_angle": { - "default": 45 - }, - "machine_heat_zone_length": { - "default": 16 - } - } - }, - "machine_settings": { - "machine_start_gcode" : { "default": "" }, - "machine_end_gcode" : { "default": "" }, - "machine_width": { "default": 223 }, - "machine_depth": { "default": 223 }, - "machine_height": { "default": 205 }, - "machine_heated_bed": { "default": true }, - - "machine_head_with_fans_polygon": - { - "default": [ - [ - -42, - 12 - ], - [ - -42, - -32 - ], - [ - 62, - 12 - ], - [ - 62, - -32 - ] - ] - }, - "machine_center_is_zero": { "default": false }, - "machine_nozzle_size": { "default": 0.4, "min_value": "0.001"}, - "machine_nozzle_heat_up_speed": { "default": 2.0 }, - "machine_nozzle_cool_down_speed": { "default": 2.0 }, - "gantry_height": { "default": 55 }, - "machine_use_extruder_offset_to_offset_coords": { "default": true }, - "machine_gcode_flavor": { "default": "UltiGCode" }, - "machine_disallowed_areas": { "default": [ - [[-115.0, 112.5], [ -82.0, 112.5], [ -84.0, 102.5], [-115.0, 102.5]], - [[ 115.0, 112.5], [ 115.0, 102.5], [ 110.0, 102.5], [ 108.0, 112.5]], - [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], - [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] - ]}, - "machine_platform_offset": { "default": [9.0, 0.0, 0.0] }, - - "machine_nozzle_tip_outer_diameter": { "default": 1.0 }, - "machine_nozzle_head_distance": { "default": 3.0 }, - "machine_nozzle_expansion_angle": { "default": 45 } - }, - - "overrides": { - "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" } - } -} diff --git a/resources/machines/ultimaker2_extended.json b/resources/machines/ultimaker2_extended.json deleted file mode 100644 index 0cd5ff0002..0000000000 --- a/resources/machines/ultimaker2_extended.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "id": "ultimaker2_extended", - "version": 1, - "name": "Ultimaker 2 Extended", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "icon": "icon_ultimaker2.png", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Extendedbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2.json", - - "machine_settings": { - "machine_height": { "default": 315 } - } -} diff --git a/resources/machines/ultimaker2_extended_plus.json b/resources/machines/ultimaker2_extended_plus.json deleted file mode 100644 index 318361a894..0000000000 --- a/resources/machines/ultimaker2_extended_plus.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2_extended_plus_base", - "version": 1, - "name": "Ultimaker 2 Extended+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2ExtendedPlusbackplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2plus.json", - - "machine_settings": { - "machine_height": { "default": 313 }, - "machine_show_variants": { "default": true }, - "machine_nozzle_head_distance": { "default": 5 }, - "machine_nozzle_expansion_angle": { "default": 45 } - } -} diff --git a/resources/machines/ultimaker2_extended_plus_025.json b/resources/machines/ultimaker2_extended_plus_025.json deleted file mode 100644 index 187079aa33..0000000000 --- a/resources/machines/ultimaker2_extended_plus_025.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "ultimaker2_extended_plus", - "version": 1, - "name": "Ultimaker 2 Extended+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2ExtendedPlusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_plus.json", - "variant": "0.25 mm", - "profiles_machine": "ultimaker2plus", - "machine_settings": { - "machine_nozzle_size": { "default": 0.25 }, - "machine_nozzle_tip_outer_diameter": { "default": 0.8 } - } -} diff --git a/resources/machines/ultimaker2_extended_plus_040.json b/resources/machines/ultimaker2_extended_plus_040.json deleted file mode 100644 index b548bbe423..0000000000 --- a/resources/machines/ultimaker2_extended_plus_040.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "ultimaker2_extended_plus", - "version": 1, - "name": "Ultimaker 2 Extended+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2ExtendedPlusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_plus.json", - "variant": "0.4 mm", - "profiles_machine": "ultimaker2plus", - "machine_settings": { - "machine_nozzle_size": { "default": 0.40 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.05 } - } -} diff --git a/resources/machines/ultimaker2_extended_plus_060.json b/resources/machines/ultimaker2_extended_plus_060.json deleted file mode 100644 index 9d39c267ba..0000000000 --- a/resources/machines/ultimaker2_extended_plus_060.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "ultimaker2_extended_plus", - "version": 1, - "name": "Ultimaker 2 Extended+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2ExtendedPlusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_plus.json", - "variant": "0.6 mm", - "profiles_machine": "ultimaker2plus", - "machine_settings": { - "machine_nozzle_size": { "default": 0.60 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.25 } - } -} diff --git a/resources/machines/ultimaker2_extended_plus_080.json b/resources/machines/ultimaker2_extended_plus_080.json deleted file mode 100644 index bf74998f57..0000000000 --- a/resources/machines/ultimaker2_extended_plus_080.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "ultimaker2_extended_plus", - "version": 1, - "name": "Ultimaker 2 Extended+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2ExtendedPlusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_plus.json", - "variant": "0.8 mm", - "profiles_machine": "ultimaker2plus", - "machine_settings": { - "machine_nozzle_size": { "default": 0.80 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.35 } - } -} diff --git a/resources/machines/ultimaker2_go.json b/resources/machines/ultimaker2_go.json deleted file mode 100644 index cc2cde09d7..0000000000 --- a/resources/machines/ultimaker2_go.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id": "ultimaker2_go", - "version": 1, - "name": "Ultimaker 2 Go", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "icon": "icon_ultimaker2.png", - "platform": "ultimaker2go_platform.obj", - "platform_texture": "Ultimaker2Gobackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2.json", - - "overrides": { - "machine_width": { "default": 120 }, - "machine_depth": { "default": 120 }, - "machine_height": { "default": 115 }, - "machine_heated_bed": { "default": false }, - "machine_disallowed_areas": { "default": [ - [[-60.0, 60.0], [-33.0, 60.0], [-35.0, 52.0], [-60.0, 52.0]], - [[ 60.0, 60.0], [ 60.0, 52.0], [ 35.0, 52.0], [ 33.0, 60.0]], - [[-60.0, -60.0], [-60.0, -52.0], [-35.0, -52.0], [-33.0, -60.0]], - [[ 60.0, -60.0], [ 33.0, -60.0], [ 35.0, -52.0], [ 60.0, -52.0]] - ]}, - "machine_platform_offset": { "default": [0.0, 0.0, 0.0] } - } -} diff --git a/resources/machines/ultimaker2plus.json b/resources/machines/ultimaker2plus.json deleted file mode 100644 index a3441a8dd4..0000000000 --- a/resources/machines/ultimaker2plus.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "id": "ultimaker2plus_base", - "version": 1, - "name": "Ultimaker 2+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Plusbackplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2.json", - - "overrides": { - "machine_height": { "default": 203 }, - "machine_show_variants": { "default": true }, - "gantry_height": { "default": 52 }, - "machine_nozzle_head_distance": { "default": 5 }, - "machine_nozzle_expansion_angle": { "default": 45 }, - "machine_heat_zone_length": { "default": 20 }, - "machine_head_with_fans_polygon": - { - "default": [ - [ - -44, - 14 - ], - [ - -44, - -34 - ], - [ - 64, - 14 - ], - [ - 64, - -34 - ] - ] - }, - "retraction_amount": { "default": 6.0 }, - "machine_disallowed_areas": { "default": [ - [[-115.0, 112.5], [ -78.0, 112.5], [ -80.0, 102.5], [-115.0, 102.5]], - [[ 115.0, 112.5], [ 115.0, 102.5], [ 105.0, 102.5], [ 103.0, 112.5]], - [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], - [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] - ]} - } -} diff --git a/resources/machines/ultimaker2plus_025.json b/resources/machines/ultimaker2plus_025.json deleted file mode 100644 index 22bb33cb55..0000000000 --- a/resources/machines/ultimaker2plus_025.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "id": "ultimaker2plus", - "version": 1, - "name": "Ultimaker 2+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Plusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2plus.json", - - "variant": "0.25 mm", - - "overrides": { - "machine_nozzle_size": { "default": 0.25 }, - "machine_nozzle_tip_outer_diameter": { "default": 0.8 }, - "coasting_volume": { "default": 0.1 }, - "coasting_min_volume": { "default": 0.17 } - } -} diff --git a/resources/machines/ultimaker2plus_040.json b/resources/machines/ultimaker2plus_040.json deleted file mode 100644 index 29fc1a75f3..0000000000 --- a/resources/machines/ultimaker2plus_040.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "ultimaker2plus", - "version": 1, - "name": "Ultimaker 2+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Plusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2plus.json", - - "variant": "0.4 mm", - - "overrides": { - "machine_nozzle_size": { "default": 0.40 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.05 } - } -} diff --git a/resources/machines/ultimaker2plus_060.json b/resources/machines/ultimaker2plus_060.json deleted file mode 100644 index cfcc2ab4f2..0000000000 --- a/resources/machines/ultimaker2plus_060.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2plus", - "version": 1, - "name": "Ultimaker 2+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Plusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2plus.json", - - "variant": "0.6 mm", - - "overrides": { - "machine_nozzle_size": { "default": 0.60 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.25 }, - "coasting_volume": { "default": 1.36 } - } -} diff --git a/resources/machines/ultimaker2plus_080.json b/resources/machines/ultimaker2plus_080.json deleted file mode 100644 index 2f2cd2571c..0000000000 --- a/resources/machines/ultimaker2plus_080.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2plus", - "version": 1, - "name": "Ultimaker 2+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Plusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker2plus.json", - - "variant": "0.8 mm", - - "overrides": { - "machine_nozzle_size": { "default": 0.80 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.35 }, - "coasting_volume": { "default": 3.22 } - } -} diff --git a/resources/machines/ultimaker_original.json b/resources/machines/ultimaker_original.json deleted file mode 100644 index 3e694c1b8e..0000000000 --- a/resources/machines/ultimaker_original.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "id": "ultimaker_original", - "version": 1, - "name": "Ultimaker Original", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "icon": "icon_ultimaker.png", - "platform": "ultimaker_platform.stl", - "file_formats": "text/x-gcode", - "inherits": "ultimaker.json", - - "pages": [ - "SelectUpgradedParts", - "UpgradeFirmware", - "UltimakerCheckup", - "BedLeveling" - ], - - "machine_extruder_trains": { - "0": { - "machine_nozzle_heat_up_speed": { - "default": 2.0 - }, - "machine_nozzle_cool_down_speed": { - "default": 2.0 - }, - "machine_nozzle_tip_outer_diameter": { - "default": 1 - }, - "machine_nozzle_head_distance": { - "default": 3 - }, - "machine_nozzle_expansion_angle": { - "default": 45 - }, - "machine_heat_zone_length": { - "default": 16 - } - } - }, - "overrides": { - "machine_width": { "default": 205 }, - "machine_height": { "default": 200 }, - "machine_depth": { "default": 205 }, - "machine_center_is_zero": { "default": false }, - "machine_nozzle_size": { "default": 0.4 }, - "machine_nozzle_heat_up_speed": { "default": 2.0 }, - "machine_nozzle_cool_down_speed": { "default": 2.0 }, - "machine_head_with_fans_polygon": - { - "default": [ - [ - -75, - 35 - ], - [ - -75, - -18 - ], - [ - 18, - 35 - ], - [ - 18, - -18 - ] - ] - }, - "gantry_height": { "default": 55 }, - "machine_use_extruder_offset_to_offset_coords": { "default": true }, - "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 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": "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" - }, - - "machine_extruder_drive_upgrade": { "default": false } - } -} diff --git a/resources/machines/ultimaker_original_plus.json b/resources/machines/ultimaker_original_plus.json deleted file mode 100644 index 07c5a04549..0000000000 --- a/resources/machines/ultimaker_original_plus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "id": "ultimaker_original_plus", - "version": 1, - "name": "Ultimaker Original+", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "icon": "icon_ultimaker.png", - "platform": "ultimaker2_platform.obj", - "platform_texture": "UltimakerPlusbackplate.png", - "file_formats": "text/x-gcode", - "inherits": "ultimaker_original.json", - - "pages": [ - "UpgradeFirmware", - "UltimakerCheckup", - "BedLeveling" - ], - - "overrides": { - "machine_heated_bed": { "default": true } - } -} diff --git a/resources/materials/abs.inst.cfg b/resources/materials/abs.inst.cfg new file mode 100644 index 0000000000..0d64e81437 --- /dev/null +++ b/resources/materials/abs.inst.cfg @@ -0,0 +1,12 @@ +[general] +version = 2 +name = ABS +definition = fdmprinter + +[metadata] +type = material + +[values] +material_print_temperature = 250 +material_bed_temperature = 80 +material_flow = 107 diff --git a/resources/materials/cpe.inst.cfg b/resources/materials/cpe.inst.cfg new file mode 100644 index 0000000000..ca30cba046 --- /dev/null +++ b/resources/materials/cpe.inst.cfg @@ -0,0 +1,11 @@ +[general] +version = 2 +name = CPE +definition = fdmprinter + +[metadata] +type = material + +[values] +material_print_temperature = 250 +material_bed_temperature = 70 \ No newline at end of file diff --git a/resources/materials/generic_pla.xml.fdm_material b/resources/materials/generic_pla.xml.fdm_material new file mode 100644 index 0000000000..a4fe02c195 --- /dev/null +++ b/resources/materials/generic_pla.xml.fdm_material @@ -0,0 +1,47 @@ + + + + + + Generic + PLA + Generic + + 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 0 + #FFFFFF + + + 1.3 + 2.85 + + + 210 + 60 + 175 + + + + + 150 + + + + + + + + + + 150 + + 80 + + + 100 + + + + diff --git a/resources/materials/pla.inst.cfg b/resources/materials/pla.inst.cfg new file mode 100644 index 0000000000..dfa9c62469 --- /dev/null +++ b/resources/materials/pla.inst.cfg @@ -0,0 +1,10 @@ +[general] +version = 2 +name = PLA +definition = fdmprinter + +[metadata] +type = material + +[values] +material_bed_temperature = 60 diff --git a/resources/profiles/materials/abs.cfg b/resources/profiles/materials/abs.cfg index 3d05d7003a..67abc32810 100644 --- a/resources/profiles/materials/abs.cfg +++ b/resources/profiles/materials/abs.cfg @@ -4,7 +4,6 @@ type = material name = ABS [settings] -material_bed_temperature = 100 -material_flow = 107 material_print_temperature = 250 -cool_fan_speed = 50 +material_bed_temperature = 80 +material_flow = 107 diff --git a/resources/profiles/materials/cpe.cfg b/resources/profiles/materials/cpe.cfg index 3f1b33d74a..0621260745 100644 --- a/resources/profiles/materials/cpe.cfg +++ b/resources/profiles/materials/cpe.cfg @@ -5,4 +5,4 @@ name = CPE [settings] material_print_temperature = 250 -cool_fan_speed = 50 +material_bed_temperature = 70 \ No newline at end of file diff --git a/resources/profiles/materials/pla.cfg b/resources/profiles/materials/pla.cfg index 8aead264d8..b5af61b9b6 100644 --- a/resources/profiles/materials/pla.cfg +++ b/resources/profiles/materials/pla.cfg @@ -4,3 +4,4 @@ type = material name = PLA [settings] +material_bed_temperature = 60 \ No newline at end of file diff --git a/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile b/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile index 15632a183b..9f45e9d01a 100644 --- a/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile @@ -1,6 +1,6 @@ [general] version = 1 -name = Normal Quality +name = High Quality machine_type = ultimaker2plus machine_variant = 0.25 mm material = ABS @@ -9,17 +9,12 @@ weight = -2 [settings] layer_height = 0.06 wall_thickness = 0.88 -line_width = 0.22 top_bottom_thickness = 0.72 infill_sparse_density = 22 -speed_print = 20 -layer_height_0 = 0.15 -speed_layer_0 = 20 -speed_infill = 30 -speed_topbottom = 20 -speed_wall_x = 25 -cool_min_layer_time = 2 -cool_min_speed = 10 -cool_lift_head = True +speed_print = 30 +cool_min_layer_time = 3 cool_fan_speed_min = 20 +cool_min_speed = 10 +cool_min_layer_time_fan_speed_max = 15 + diff --git a/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile b/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile index 03c5cad45f..50018372b5 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile @@ -9,18 +9,13 @@ weight = -1 [settings] layer_height = 0.15 wall_thickness = 0.7 -line_width = 0.35 top_bottom_thickness = 0.75 infill_sparse_density = 18 -speed_print = 40 -layer_height_0 = 0.26 -speed_layer_0 = 30 +speed_print = 55 speed_travel = 150 -speed_infill = 55 -speed_topbottom = 30 -speed_wall_0 = 30 +speed_layer_0 = 30 cool_min_layer_time = 3 -cool_min_speed = 20 -cool_lift_head = True -cool_fan_speed_min = 50 +cool_fan_speed_min = 20 +cool_min_speed = 10 +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile b/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile index 30867e8605..341c9cc34f 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile @@ -9,17 +9,11 @@ weight = -3 [settings] layer_height = 0.06 wall_thickness = 1.05 -line_width = 0.35 top_bottom_thickness = 0.72 infill_sparse_density = 22 -speed_print = 30 -layer_height_0 = 0.26 -speed_layer_0 = 20 -speed_infill = 45 -speed_topbottom = 20 -speed_wall_0 = 20 +speed_print = 45 cool_min_layer_time = 3 -cool_min_speed = 10 cool_fan_speed_min = 20 -cool_lift_head = True +cool_min_speed = 10 +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile b/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile index a073713649..d8fce8a4dd 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile @@ -9,17 +9,10 @@ weight = -2 [settings] layer_height = 0.1 wall_thickness = 1.05 -line_width = 0.35 top_bottom_thickness = 0.8 infill_sparse_density = 20 -speed_print = 30 -layer_height_0 = 0.26 -speed_layer_0 = 20 -speed_infill = 45 -speed_topbottom = 20 -speed_wall_0 = 20 +speed_print = 45 cool_min_layer_time = 3 -cool_fan_speed_min = 50 +cool_fan_speed_min = 20 cool_min_speed = 10 -cool_lift_head = True - +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile b/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile index 7f14ef04a2..5512450471 100644 --- a/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile @@ -9,18 +9,12 @@ weight = -2 [settings] layer_height = 0.15 wall_thickness = 1.59 -line_width = 0.53 top_bottom_thickness = 1.2 infill_sparse_density = 20 -speed_print = 25 -layer_height_0 = 0.39 -speed_layer_0 = 20 -speed_infill = 40 -speed_topbottom = 20 -speed_wall_0 = 20 -speed_wall_x = 30 +speed_print = 40 cool_min_layer_time = 3 cool_fan_speed_min = 50 cool_min_speed = 20 -cool_lift_head = True +cool_min_layer_time_fan_speed_max = 20 + diff --git a/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile b/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile index 7851f82cee..e5f27c51a2 100644 --- a/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile +++ b/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile @@ -1,6 +1,6 @@ [general] version = 1 -name = Normal Quality +name = Fast Print machine_type = ultimaker2plus machine_variant = 0.8 mm material = ABS @@ -9,17 +9,11 @@ weight = -2 [settings] layer_height = 0.2 wall_thickness = 2.1 -line_width = 0.7 -speed_print = 20 top_bottom_thickness = 1.2 infill_sparse_density = 20 -layer_height_0 = 0.5 -speed_layer_0 = 20 -speed_infill = 40 -speed_topbottom = 20 -speed_wall_x = 30 +speed_print = 40 cool_min_layer_time = 3 cool_fan_speed_min = 50 cool_min_speed = 15 -cool_lift_head = True +cool_min_layer_time_fan_speed_max = 25 diff --git a/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile b/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile index 7003b825b5..c4c09932d8 100644 --- a/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile @@ -1,6 +1,6 @@ [general] version = 1 -name = Normal Quality +name = High Quality machine_type = ultimaker2plus machine_variant = 0.25 mm material = CPE @@ -9,18 +9,11 @@ weight = -2 [settings] layer_height = 0.06 wall_thickness = 0.88 -line_width = 0.22 top_bottom_thickness = 0.72 infill_sparse_density = 22 -speed_print = 20 -layer_height_0 = 0.15 -speed_layer_0 = 20 -speed_infill = 30 -speed_topbottom = 20 -speed_wall_x = 25 +speed_print = 30 cool_min_layer_time = 2 -cool_min_speed = 10 -cool_fan_speed_min = 50 -cool_lift_head = True -infill_overlap = 17 +cool_fan_speed_min = 20 +cool_min_speed = 15 +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile b/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile index 41da312634..f9050e5ce5 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile @@ -9,18 +9,14 @@ weight = -1 [settings] layer_height = 0.15 wall_thickness = 0.7 -line_width = 0.35 top_bottom_thickness = 0.75 infill_sparse_density = 18 -speed_print = 40 -layer_height_0 = 0.26 +speed_print = 45 speed_travel = 150 speed_layer_0 = 30 -speed_infill = 45 -speed_wall_0 = 30 cool_min_layer_time = 3 -cool_fan_speed_min = 50 +cool_fan_speed_min = 80 cool_min_speed = 10 -cool_lift_head = True -infill_overlap = 17 +cool_min_layer_time_fan_speed_max = 15 + diff --git a/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile b/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile index bfa93ff46c..377ab5b257 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile @@ -9,19 +9,10 @@ weight = -3 [settings] layer_height = 0.06 wall_thickness = 1.05 -line_width = 0.35 top_bottom_thickness = 0.72 infill_sparse_density = 22 -speed_print = 20 -layer_height_0 = 0.26 -speed_layer_0 = 20 -speed_infill = 45 -speed_topbottom = 20 -speed_wall_x = 30 -cool_min_layer_time = 3 -cool_fan_speed_min = 50 -cool_min_speed = 10 -cool_lift_head = True -infill_overlap = 15 - - +speed_print = 45 +cool_min_layer_time = 2 +cool_fan_speed_min = 80 +cool_min_speed = 15 +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile b/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile index b725a17713..e8142405ff 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile @@ -7,19 +7,13 @@ material = CPE weight = -2 [settings] +layer_height = 0.1 wall_thickness = 1.05 -line_width = 0.35 top_bottom_thickness = 0.8 -speed_print = 30 -layer_height_0 = 0.26 -speed_layer_0 = 20 -speed_infill = 45 -speed_topbottom = 20 -speed_wall_0 = 20 -speed_wall_x = 30 +infill_sparse_density = 20 +speed_print = 45 cool_min_layer_time = 3 -cool_fan_speed_min = 50 +cool_fan_speed_min = 80 cool_min_speed = 10 -cool_lift_head = True -infill_overlap = 15 +cool_min_layer_time_fan_speed_max = 15 diff --git a/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile b/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile index e45bf7ef3f..034fa17e1b 100644 --- a/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile @@ -9,18 +9,10 @@ weight = -2 [settings] layer_height = 0.15 wall_thickness = 1.59 -line_width = 0.53 top_bottom_thickness = 1.2 -speed_print = 25 -layer_height_0 = 0.4 -speed_layer_0 = 20 -speed_infill = 40 -speed_topbottom = 20 -speed_wall_0 = 20 -speed_wall_x = 30 -cool_min_layer_time = 3 -cool_min_speed = 10 -cool_fan_speed_min = 50 -cool_lift_head = True -infill_overlap = 17 - +infill_sparse_density = 20 +speed_print = 40 +cool_min_layer_time = 5 +cool_fan_speed_min = 80 +cool_min_speed = 8 +cool_min_layer_time_fan_speed_max = 20 diff --git a/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile b/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile index 19cbc9a18e..523a5d3243 100644 --- a/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile +++ b/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile @@ -1,6 +1,6 @@ [general] version = 1 -name = Normal Quality +name = Fast Print machine_type = ultimaker2plus machine_variant = 0.8 mm material = CPE @@ -9,17 +9,10 @@ weight = -2 [settings] layer_height = 0.2 wall_thickness = 2.1 -line_width = 0.7 top_bottom_thickness = 1.2 -speed_print = 20 -layer_height_0 = 0.5 -speed_layer_0 = 20 -speed_infill = 40 -speed_topbottom = 20 -speed_wall_x = 30 +infill_sparse_density = 20 +speed_print = 40 cool_min_layer_time = 3 -cool_min_speed = 10 -cool_fan_speed_min = 50 -cool_lift_head = True -infill_overlap = 17 - +cool_fan_speed_min = 80 +cool_min_speed = 8 +cool_min_layer_time_fan_speed_max = 25 diff --git a/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile b/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile index d5c27687a5..63c1fc9fdd 100644 --- a/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile @@ -1,22 +1,16 @@ [general] version = 1 -name = Normal Quality +name = High Quality machine_type = ultimaker2plus machine_variant = 0.25 mm material = PLA weight = -2 [settings] -line_width = 0.22 layer_height = 0.06 -layer_height_0 = 0.15 wall_thickness = 0.88 top_bottom_thickness = 0.72 infill_sparse_density = 22 -retraction_amount = 6 -speed_print = 20 -speed_infill = 30 -speed_wall_x = 25 -speed_topbottom = 20 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 15 +speed_print = 30 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile b/resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile index 5088c055c2..06e401c139 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile @@ -7,17 +7,12 @@ material = PLA weight = -1 [settings] -line_width = 0.35 layer_height = 0.15 -layer_height_0 = 0.26 wall_thickness = 0.7 -top_bottom_thickness = 0.6 +top_bottom_thickness = 0.75 infill_sparse_density = 18 -retraction_amount = 5.5 -speed_print = 40 -speed_infill = 60 -speed_wall_x = 50 +speed_print = 60 speed_travel = 150 -speed_topbottom = 30 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 15 +speed_layer_0 = 30 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile b/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile index 5140474e48..5e2f762354 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile @@ -7,16 +7,10 @@ material = PLA weight = -3 [settings] -line_width = 0.35 layer_height = 0.06 -layer_height_0 = 0.26 wall_thickness = 1.05 -top_bottom_thickness = 0.84 +top_bottom_thickness = 0.72 infill_sparse_density = 22 -retraction_amount = 5.5 -speed_print = 30 -speed_infill = 50 -speed_wall_x = 40 -speed_topbottom = 20 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 15 \ No newline at end of file +speed_print = 50 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile b/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile index 88862832b2..689a3251b2 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile @@ -7,15 +7,10 @@ material = PLA weight = -2 [settings] -line_width = 0.35 -layer_height_0 = 0.26 +layer_height = 0.1 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 -retraction_amount = 5.5 -speed_print = 30 -speed_infill = 50 -speed_wall_x = 40 -speed_topbottom = 20 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 15 +speed_print = 50 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile b/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile index 5cb807de01..db091d8e8d 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile @@ -13,8 +13,6 @@ layer_height_0 = 0.26 wall_thickness = 1.4 top_bottom_thickness = 1.12 infill_sparse_density = 25 -retraction_amount = 5.5 -retraction_extrusion_window = 6 speed_print = 30 speed_infill = 50 speed_wall_x = 40 diff --git a/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile b/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile index 13d1c783d6..188ed42a95 100644 --- a/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile @@ -7,15 +7,10 @@ material = PLA weight = -2 [settings] -line_width = 0.53 layer_height = 0.15 -layer_height_0 = 0.4 wall_thickness = 1.59 top_bottom_thickness = 1.2 -retraction_amount = 6 -speed_print = 25 -speed_infill = 55 -speed_wall_x = 40 -speed_topbottom = 20 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 20 +infill_sparse_density = 20 +speed_print = 55 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile b/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile index 030bd38064..92cb4a6054 100644 --- a/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile +++ b/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile @@ -1,22 +1,16 @@ [general] version = 1 -name = Normal Quality +name = Fast Print machine_type = ultimaker2plus machine_variant = 0.8 mm material = PLA weight = -2 [settings] -line_width = 0.7 layer_height = 0.2 -layer_height_0 = 0.5 wall_thickness = 2.1 -top_bottom_thickness = 1.6 +top_bottom_thickness = 1.2 infill_sparse_density = 20 -retraction_amount = 6 -speed_print = 20 -speed_infill = 40 -speed_wall_x = 30 -speed_topbottom = 20 -speed_layer_0 = 25 -cool_min_layer_time_fan_speed_max = 25 +speed_print = 40 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index c363dd4a55..f3bce7b993 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -1,6 +1,8 @@ // Copyright (c) 2015 Ultimaker B.V. // Cura is released under the terms of the AGPLv3 or higher. +pragma Singleton + import QtQuick 2.2 import QtQuick.Controls 1.1 import UM 1.1 as UM @@ -21,7 +23,7 @@ Item property alias unGroupObjects:unGroupObjectsAction; property alias mergeObjects: mergeObjectsAction; //property alias unMergeObjects: unMergeObjectsAction; - + property alias multiplyObject: multiplyObjectAction; property alias deleteAll: deleteAllAction; @@ -32,6 +34,8 @@ Item property alias addMachine: addMachineAction; property alias configureMachines: settingsAction; property alias addProfile: addProfileAction; + property alias updateProfile: updateProfileAction; + property alias resetProfile: resetProfileAction; property alias manageProfiles: manageProfilesAction; property alias preferences: preferencesAction; @@ -43,6 +47,8 @@ Item property alias toggleFullScreen: toggleFullScreenAction; + property alias configureSettingVisibility: configureSettingVisibilityAction + UM.I18nCatalog{id: catalog; name:"cura"} Action @@ -58,6 +64,8 @@ Item text: catalog.i18nc("@action:inmenu menubar:edit","&Undo"); iconName: "edit-undo"; shortcut: StandardKey.Undo; + onTriggered: UM.OperationStack.undo(); + enabled: UM.OperationStack.canUndo; } Action @@ -66,6 +74,8 @@ Item text: catalog.i18nc("@action:inmenu menubar:edit","&Redo"); iconName: "edit-redo"; shortcut: StandardKey.Redo; + onTriggered: UM.OperationStack.redo(); + enabled: UM.OperationStack.canRedo; } Action @@ -96,11 +106,27 @@ Item iconName: "configure"; } + Action + { + id: updateProfileAction; + enabled: UM.ActiveProfile.valid && !UM.ActiveProfile.readOnly && UM.ActiveProfile.hasCustomisedValues + text: catalog.i18nc("@action:inmenu menubar:profile","&Update Current Profile"); + onTriggered: UM.ActiveProfile.updateProfile(); + } + + Action + { + id: resetProfileAction; + enabled: UM.ActiveProfile.valid && UM.ActiveProfile.hasCustomisedValues + text: catalog.i18nc("@action:inmenu menubar:profile","&Reload Current Profile"); + onTriggered: UM.ActiveProfile.discardChanges(); + } + Action { id: addProfileAction; enabled: UM.ActiveProfile.valid - text: catalog.i18nc("@action:inmenu menubar:profile","&Add Profile..."); + text: catalog.i18nc("@action:inmenu menubar:profile","&Create New Profile..."); } Action @@ -116,12 +142,14 @@ Item text: catalog.i18nc("@action:inmenu menubar:help","Show Online &Documentation"); iconName: "help-contents"; shortcut: StandardKey.Help; + onTriggered: CuraActions.openDocumentation(); } Action { id: reportBugAction; text: catalog.i18nc("@action:inmenu menubar:help","Report a &Bug"); iconName: "tools-report-bug"; + onTriggered: CuraActions.openBugReportPage(); } Action @@ -138,6 +166,7 @@ Item enabled: UM.Controller.toolsEnabled; iconName: "edit-delete"; shortcut: StandardKey.Delete; + onTriggered: Printer.deleteSelection(); } Action @@ -160,6 +189,8 @@ Item text: catalog.i18nc("@action:inmenu menubar:edit","&Group Objects"); enabled: UM.Scene.numObjectsSelected > 1 ? true: false iconName: "object-group" + shortcut: "Ctrl+G"; + onTriggered: Printer.groupSelected(); } Action @@ -168,14 +199,18 @@ Item text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Objects"); enabled: UM.Scene.isGroupSelected iconName: "object-ungroup" + shortcut: "Ctrl+Shift+G"; + onTriggered: Printer.ungroupSelected(); } - + Action { id: mergeObjectsAction text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Objects"); enabled: UM.Scene.numObjectsSelected > 1 ? true: false iconName: "merge"; + shortcut: "Ctrl+Alt+G"; + onTriggered: Printer.mergeSelected(); } Action @@ -192,6 +227,7 @@ Item enabled: UM.Controller.toolsEnabled; iconName: "edit-delete"; shortcut: "Ctrl+D"; + onTriggered: Printer.deleteAll(); } Action @@ -199,18 +235,21 @@ Item id: reloadAllAction; text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Objects"); iconName: "document-revert"; + onTriggered: Printer.reloadAll(); } Action { id: resetAllTranslationAction; text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object Positions"); + onTriggered: Printer.resetAllTranslation(); } Action { id: resetAllAction; text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object &Transformations"); + onTriggered: Printer.resetAll(); } Action @@ -228,4 +267,10 @@ Item iconName: "view-list-text"; shortcut: StandardKey.WhatsThis; } + + Action + { + id: configureSettingVisibilityAction + text: catalog.i18nc("@action:menu", "Configure setting visiblity..."); + } } diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml new file mode 100644 index 0000000000..4a5e14f183 --- /dev/null +++ b/resources/qml/AddMachineDialog.qml @@ -0,0 +1,178 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import QtQuick.Controls.Styles 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +UM.Dialog +{ + id: base + title: catalog.i18nc("@title:window", "Add Printer") + property string activeManufacturer: "Ultimaker"; + + function getMachineName() + { + var name = machineList.model.getItem(machineList.currentIndex).name + return name + } + + ScrollView + { + id: machinesHolder + + anchors + { + left: parent.left; + top: parent.top; + right: parent.right; + bottom: parent.bottom; + } + ListView + { + id: machineList + + model: UM.DefinitionContainersModel + { + id: machineDefinitionsModel + filter: {"visible":true} + } + section.property: "manufacturer" + section.delegate: Button + { + text: section + style: ButtonStyle + { + background: Rectangle + { + border.width: 0 + color: "transparent"; + height: UM.Theme.getSize("standard_list_lineheight").height + width: machineList.width + } + label: Label + { + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("standard_arrow").width + UM.Theme.getSize("default_margin").width + text: control.text + color: palette.windowText + font.bold: true + UM.RecolorImage + { + id: downArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: width + color: palette.windowText + source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") + } + } + } + + onClicked: + { + base.activeManufacturer = section; + machineList.currentIndex = machineList.model.find("manufacturer", section) + machineName.text = getMachineName() + } + } + + delegate: RadioButton + { + id: machineButton + + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("standard_list_lineheight").width + + opacity: 1; + height: UM.Theme.getSize("standard_list_lineheight").height; + + checked: ListView.isCurrentItem; + + exclusiveGroup: printerGroup; + + text: model.name + + onClicked: + { + ListView.view.currentIndex = index; + machineName.text = getMachineName() + } + + states: State + { + name: "collapsed"; + when: base.activeManufacturer != model.manufacturer; + + PropertyChanges { target: machineButton; opacity: 0; height: 0; } + } + + transitions: + [ + Transition + { + to: "collapsed"; + SequentialAnimation + { + NumberAnimation { property: "opacity"; duration: 75; } + NumberAnimation { property: "height"; duration: 75; } + } + }, + Transition + { + from: "collapsed"; + SequentialAnimation + { + NumberAnimation { property: "height"; duration: 75; } + NumberAnimation { property: "opacity"; duration: 75; } + } + } + ] + } + } + } + + TextField + { + id: machineName; + text: getMachineName() + implicitWidth: UM.Theme.getSize("standard_list_input").width + maximumLength: 40 + anchors.bottom:parent.bottom + } + + Button + { + text:"save" + anchors.bottom: parent.bottom + anchors.right: parent.right + onClicked: + { + base.visible = false + var item = machineList.model.getItem(machineList.currentIndex); + Cura.MachineManager.addMachine(machineName.text, item.id) + } + } + + Item + { + UM.I18nCatalog + { + id: catalog; + name: "cura"; + } + SystemPalette { id: palette } + ExclusiveGroup { id: printerGroup; } + } +} diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 776e83cb60..a7f4a43c22 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -7,7 +7,10 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.1 -import UM 1.1 as UM +import UM 1.2 as UM +import Cura 1.0 as Cura + +import "." UM.MainWindow { @@ -53,7 +56,7 @@ UM.MainWindow title: catalog.i18nc("@title:menu menubar:toplevel","&File"); MenuItem { - action: actions.open; + action: Actions.open; } Menu @@ -115,11 +118,11 @@ UM.MainWindow } } - MenuItem { action: actions.reloadAll; } + MenuItem { action: Actions.reloadAll; } MenuSeparator { } - MenuItem { action: actions.quit; } + MenuItem { action: Actions.quit; } } Menu @@ -127,27 +130,27 @@ UM.MainWindow //: Edit menu title: catalog.i18nc("@title:menu menubar:toplevel","&Edit"); - MenuItem { action: actions.undo; } - MenuItem { action: actions.redo; } + MenuItem { action: Actions.undo; } + MenuItem { action: Actions.redo; } MenuSeparator { } - MenuItem { action: actions.deleteSelection; } - MenuItem { action: actions.deleteAll; } - MenuItem { action: actions.resetAllTranslation; } - MenuItem { action: actions.resetAll; } + MenuItem { action: Actions.deleteSelection; } + MenuItem { action: Actions.deleteAll; } + MenuItem { action: Actions.resetAllTranslation; } + MenuItem { action: Actions.resetAll; } MenuSeparator { } - MenuItem { action: actions.groupObjects;} - MenuItem { action: actions.mergeObjects;} - MenuItem { action: actions.unGroupObjects;} + MenuItem { action: Actions.groupObjects;} + MenuItem { action: Actions.mergeObjects;} + MenuItem { action: Actions.unGroupObjects;} } Menu { title: catalog.i18nc("@title:menu menubar:toplevel","&View"); id: top_view_menu - Instantiator + Instantiator { model: UM.ViewModel { } - MenuItem + MenuItem { text: model.name; checkable: true; @@ -168,14 +171,17 @@ UM.MainWindow Instantiator { - model: UM.MachineInstancesModel { } + model: UM.ContainerStacksModel + { + filter: {"type": "machine"} + } MenuItem { text: model.name; checkable: true; - checked: model.active; - exclusiveGroup: machineMenuGroup; - onTriggered: UM.MachineManager.setActiveMachineInstance(model.name) + checked: Cura.MachineManager.activeMachineId == model.id + exclusiveGroup: machineSelectionMenuGroup; + onTriggered: Cura.MachineManager.setActiveMachine(model.id); } onObjectAdded: machineMenu.insertItem(index, object) onObjectRemoved: machineMenu.removeItem(object) @@ -187,13 +193,20 @@ UM.MainWindow Instantiator { - model: UM.MachineVariantsModel { } + model: UM.InstanceContainersModel + { + filter: + { + "type": "variant", + "definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine + } + } MenuItem { text: model.name; checkable: true; - checked: model.active; + checked: model.id == Cura.MachineManager.activeVariantId; exclusiveGroup: machineVariantsGroup; - onTriggered: UM.MachineManager.setActiveMachineVariant(model.name) + onTriggered: Cura.MachineManager.setActiveVariant(model.id) } onObjectAdded: machineMenu.insertItem(index, object) onObjectRemoved: machineMenu.removeItem(object) @@ -201,10 +214,10 @@ UM.MainWindow ExclusiveGroup { id: machineVariantsGroup; } - MenuSeparator { visible: UM.MachineManager.hasVariants; } + MenuSeparator { visible: Cura.MachineManager.hasVariants; } - MenuItem { action: actions.addMachine; } - MenuItem { action: actions.configureMachines; } + MenuItem { action: Actions.addMachine; } + MenuItem { action: Actions.configureMachines; } } Menu @@ -215,7 +228,7 @@ UM.MainWindow Instantiator { id: profileMenuInstantiator - model: UM.ProfilesModel {} +// model: UM.ProfilesModel {} property int separatorIndex: -1 Loader { @@ -229,7 +242,7 @@ UM.MainWindow //Insert a separator between readonly and custom profiles if(separatorIndex < 0 && index > 0) { if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) { - profileMenu.addSeparator(); + profileMenu.insertSeparator(index); separatorIndex = index; } } @@ -277,8 +290,11 @@ UM.MainWindow MenuSeparator { id: profileMenuSeparator } - MenuItem { action: actions.addProfile; } - MenuItem { action: actions.manageProfiles; } + MenuItem { action: Actions.updateProfile; } + MenuItem { action: Actions.resetProfile; } + MenuItem { action: Actions.addProfile; } + MenuSeparator { } + MenuItem { action: Actions.manageProfiles; } } Menu @@ -287,9 +303,10 @@ UM.MainWindow //: Extensions menu title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions"); - Instantiator + Instantiator { - model: UM.Models.extensionModel + id: extenions + model: UM.ExtensionModel { } Menu { @@ -303,7 +320,7 @@ UM.MainWindow MenuItem { text: model.text - onTriggered: UM.Models.extensionModel.subMenuTriggered(name, model.text) + onTriggered: extenions.model.subMenuTriggered(name, model.text) } onObjectAdded: sub_menu.insertItem(index, object) onObjectRemoved: sub_menu.removeItem(object) @@ -320,7 +337,7 @@ UM.MainWindow //: Settings menu title: catalog.i18nc("@title:menu menubar:toplevel","&Settings"); - MenuItem { action: actions.preferences; } + MenuItem { action: Actions.preferences; } } Menu @@ -328,11 +345,11 @@ UM.MainWindow //: Help menu title: catalog.i18nc("@title:menu menubar:toplevel","&Help"); - MenuItem { action: actions.showEngineLog; } - MenuItem { action: actions.documentation; } - MenuItem { action: actions.reportBug; } + MenuItem { action: Actions.showEngineLog; } + MenuItem { action: Actions.documentation; } + MenuItem { action: Actions.reportBug; } MenuSeparator { } - MenuItem { action: actions.about; } + MenuItem { action: Actions.about; } } } @@ -422,7 +439,7 @@ UM.MainWindow left: parent.left; //leftMargin: UM.Theme.getSize("loadfile_margin").width } - action: actions.open; + action: Actions.open; } Image @@ -510,20 +527,12 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width; - addMachineAction: actions.addMachine; - configureMachinesAction: actions.configureMachines; - addProfileAction: actions.addProfile; - manageProfilesAction: actions.manageProfiles; - - configureSettingsAction: Action - { - onTriggered: - { - preferences.visible = true; - preferences.setPage(2); - preferences.getCurrentItem().scrollToSection(source.key); - } - } + addMachineAction: Actions.addMachine; + configureMachinesAction: Actions.configureMachines; + addProfileAction: Actions.addProfile; + updateProfileAction: Actions.updateProfile; + resetProfileAction: Actions.resetProfile; + manageProfilesAction: Actions.manageProfiles; } } } @@ -541,8 +550,14 @@ UM.MainWindow //: View preferences page title insertPage(1, catalog.i18nc("@title:tab","View"), Qt.resolvedUrl("ViewPage.qml")); + insertPage(3, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("MachinesPage.qml")); + + insertPage(4, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/MaterialsPage.qml")); + + insertPage(5, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml")); + //Force refresh - setPage(0) + setPage(0); } onVisibleChanged: @@ -556,83 +571,64 @@ UM.MainWindow } } - Actions + Connections { - id: actions; + target: Actions.preferences + onTriggered: preferences.visible = true + } - open.onTriggered: openDialog.open(); - - quit.onTriggered: base.visible = false; - - undo.onTriggered: UM.OperationStack.undo(); - undo.enabled: UM.OperationStack.canUndo; - redo.onTriggered: UM.OperationStack.redo(); - redo.enabled: UM.OperationStack.canRedo; - - deleteSelection.onTriggered: + Connections + { + target: Actions.addProfile + onTriggered: { - Printer.deleteSelection() - } + UM.MachineManager.createProfile(); + preferences.setPage(5); + preferences.show(); - deleteObject.onTriggered: + // Show the renameDialog after a very short delay so the preference page has time to initiate + showProfileNameDialogTimer.start(); + } + } + + Connections + { + target: Actions.configureMachines + onTriggered: { - if(objectContextMenu.objectId != 0) - { - Printer.deleteObject(objectContextMenu.objectId); - objectContextMenu.objectId = 0; - } + preferences.visible = true; + preferences.setPage(3); } + } - multiplyObject.onTriggered: + Connections + { + target: Actions.manageProfiles + onTriggered: { - if(objectContextMenu.objectId != 0) - { - Printer.multiplyObject(objectContextMenu.objectId, 1); - objectContextMenu.objectId = 0; - } + preferences.visible = true; + preferences.setPage(5); } + } - centerObject.onTriggered: + Connections + { + target: Actions.configureSettingVisibility + onTriggered: { - if(objectContextMenu.objectId != 0) - { - Printer.centerObject(objectContextMenu.objectId); - objectContextMenu.objectId = 0; - } - } - - groupObjects.onTriggered: - { - Printer.groupSelected() - } - - unGroupObjects.onTriggered: - { - Printer.ungroupSelected() - } - - mergeObjects.onTriggered: - { - Printer.mergeSelected() + preferences.visible = true; + preferences.setPage(2); + preferences.getCurrentItem().scrollToSection(source.key); } + } - deleteAll.onTriggered: Printer.deleteAll() - resetAllTranslation.onTriggered: Printer.resetAllTranslation() - resetAll.onTriggered: Printer.resetAll() - reloadAll.onTriggered: Printer.reloadAll() + Timer + { + id: showProfileNameDialogTimer + repeat: false + interval: 1 - addMachine.onTriggered: addMachineWizard.visible = true; - addProfile.onTriggered: { UM.MachineManager.createProfile(); preferences.visible = true; preferences.setPage(4); } - - preferences.onTriggered: { preferences.visible = true; } - configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(3); } - manageProfiles.onTriggered: { preferences.visible = true; preferences.setPage(4); } - - documentation.onTriggered: CuraActions.openDocumentation(); - reportBug.onTriggered: CuraActions.openBugReportPage(); - showEngineLog.onTriggered: engineLog.visible = true; - about.onTriggered: aboutDialog.visible = true; - toggleFullScreen.onTriggered: base.toggleFullscreen() + onTriggered: preferences.getCurrentItem().showProfileNameDialog() } Menu @@ -640,29 +636,70 @@ UM.MainWindow id: objectContextMenu; property variant objectId: -1; - MenuItem { action: actions.centerObject; } - MenuItem { action: actions.deleteObject; } - MenuItem { action: actions.multiplyObject; } + MenuItem { action: Actions.centerObject; } + MenuItem { action: Actions.deleteObject; } + MenuItem { action: Actions.multiplyObject; } MenuSeparator { } - MenuItem { action: actions.deleteAll; } - MenuItem { action: actions.reloadAll; } - MenuItem { action: actions.resetAllTranslation; } - MenuItem { action: actions.resetAll; } - MenuItem { action: actions.groupObjects;} - MenuItem { action: actions.mergeObjects;} - MenuItem { action: actions.unGroupObjects;} + MenuItem { action: Actions.deleteAll; } + MenuItem { action: Actions.reloadAll; } + MenuItem { action: Actions.resetAllTranslation; } + MenuItem { action: Actions.resetAll; } + MenuSeparator { } + MenuItem { action: Actions.groupObjects; } + MenuItem { action: Actions.mergeObjects; } + MenuItem { action: Actions.unGroupObjects; } + + Connections + { + target: Actions.deleteObject + onTriggered: + { + if(objectContextMenu.objectId != 0) + { + Printer.deleteObject(objectContextMenu.objectId); + objectContextMenu.objectId = 0; + } + } + } + + Connections + { + target: Actions.multiplyObject + onTriggered: + { + if(objectContextMenu.objectId != 0) + { + Printer.multiplyObject(objectContextMenu.objectId, 1); + objectContextMenu.objectId = 0; + } + } + } + + Connections + { + target: Actions.centerObject + onTriggered: + { + if(objectContextMenu.objectId != 0) + { + Printer.centerObject(objectContextMenu.objectId); + objectContextMenu.objectId = 0; + } + } + } } Menu { id: contextMenu; - MenuItem { action: actions.deleteAll; } - MenuItem { action: actions.reloadAll; } - MenuItem { action: actions.resetAllTranslation; } - MenuItem { action: actions.resetAll; } - MenuItem { action: actions.groupObjects;} - MenuItem { action: actions.mergeObjects;} - MenuItem { action: actions.unGroupObjects;} + MenuItem { action: Actions.deleteAll; } + MenuItem { action: Actions.reloadAll; } + MenuItem { action: Actions.resetAllTranslation; } + MenuItem { action: Actions.resetAll; } + MenuSeparator { } + MenuItem { action: Actions.groupObjects; } + MenuItem { action: Actions.mergeObjects; } + MenuItem { action: Actions.unGroupObjects; } } Connections @@ -681,6 +718,18 @@ UM.MainWindow } } + Connections + { + target: Actions.quit + onTriggered: base.visible = false; + } + + Connections + { + target: Actions.toggleFullScreen + onTriggered: base.toggleFullscreen(); + } + FileDialog { id: openDialog; @@ -705,14 +754,32 @@ UM.MainWindow } } + Connections + { + target: Actions.open + onTriggered: openDialog.open() + } + EngineLog { id: engineLog; } - AddMachineWizard + Connections { - id: addMachineWizard + target: Actions.showEngineLog + onTriggered: engineLog.visible = true; + } + + AddMachineDialog + { + id: addMachineDialog + } + + Connections + { + target: Actions.addMachine + onTriggered: addMachineDialog.visible = true; } AboutDialog @@ -720,13 +787,19 @@ UM.MainWindow id: aboutDialog } + Connections + { + target: Actions.about + onTriggered: aboutDialog.visible = true; + } + Connections { target: Printer onRequestAddPrinter: { - addMachineWizard.visible = true - addMachineWizard.firstRun = false + addMachineDialog.visible = true + addMachineDialog.firstRun = false } } @@ -743,10 +816,9 @@ UM.MainWindow base.visible = true; restart(); } - else if(UM.MachineManager.activeMachineInstance == "") + else if(Cura.MachineManager.activeMachineId == null || Cura.MachineManager.activeMachineId == "") { - addMachineWizard.firstRun = true; - addMachineWizard.open(); + addMachineDialog.open(); } } } diff --git a/resources/qml/GeneralPage.qml b/resources/qml/GeneralPage.qml index c22cd6fef2..dcd5c66d73 100644 --- a/resources/qml/GeneralPage.qml +++ b/resources/qml/GeneralPage.qml @@ -5,6 +5,7 @@ import QtQuick 2.1 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.1 +import QtQml.Models 2.2 import UM 1.1 as UM @@ -28,14 +29,26 @@ UM.PreferencesPage function reset() { UM.Preferences.resetPreference("general/language") - UM.Preferences.resetPreference("physics/automatic_push_free") - UM.Preferences.resetPreference("mesh/scale_to_fit") - UM.Preferences.resetPreference("info/send_slice_info") - pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) - sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info")) - scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) var defaultLanguage = UM.Preferences.getValue("general/language") setDefaultLanguage(defaultLanguage) + + UM.Preferences.resetPreference("physics/automatic_push_free") + pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) + UM.Preferences.resetPreference("mesh/scale_to_fit") + scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) + UM.Preferences.resetPreference("mesh/scale_tiny_meshes") + scaleTinyCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) + UM.Preferences.resetPreference("cura/jobname_prefix") + prefixJobNameCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) + + if (plugins.model.find("id", "SliceInfoPlugin") > -1) { + UM.Preferences.resetPreference("info/send_slice_info") + sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info")) + } + if (plugins.model.find("id", "UpdateChecker") > -1) { + UM.Preferences.resetPreference("info/automatic_update_check") + checkUpdatesCheckbox.checked = boolCheck(UM.Preferences.getValue("info/automatic_update_check")) + } } ColumnLayout @@ -111,7 +124,7 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip", "Should objects on the platform be moved so that they no longer intersect.") + text: catalog.i18nc("@info:tooltip", "Should objects on the platform be moved so that they no longer intersect?") CheckBox { @@ -139,6 +152,36 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height + text: catalog.i18nc("@info:tooltip","Should opened files be scaled up if they are extremely small?") + + CheckBox + { + id: scaleTinyCheckbox + text: catalog.i18nc("@option:check","Scale extremely small files") + checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) + onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked) + } + } + + UM.TooltipArea { + visible: plugins.model.find("id", "UpdateChecker") > -1 + width: childrenRect.width + height: visible ? childrenRect.height : 0 + text: catalog.i18nc("@info:tooltip","Should Cura check for updates when the program is started?") + + CheckBox + { + id: checkUpdatesCheckbox + text: catalog.i18nc("@option:check","Check for updates on start") + checked: boolCheck(UM.Preferences.getValue("info/automatic_update_check")) + onCheckedChanged: UM.Preferences.setValue("info/automatic_update_check", checked) + } + } + + UM.TooltipArea { + visible: plugins.model.find("id", "SliceInfoPlugin") > -1 + width: childrenRect.width + height: visible ? childrenRect.height : 0 text: catalog.i18nc("@info:tooltip","Should anonymous data about your print be sent to Ultimaker? Note, no models, IP addresses or other personally identifiable information is sent or stored.") CheckBox @@ -149,5 +192,25 @@ UM.PreferencesPage onCheckedChanged: UM.Preferences.setValue("info/send_slice_info", checked) } } + + UM.TooltipArea { + width: childrenRect.width + height: childrenRect.height + text: catalog.i18nc("@info:tooltip", "Should a prefix based on the printer name be added to the print job name automatically?") + + CheckBox + { + id: prefixJobNameCheckbox + text: catalog.i18nc("@option:check", "Add machine prefix to job name") + checked: boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) + onCheckedChanged: UM.Preferences.setValue("cura/jobname_prefix", checked) + } + } + + DelegateModel + { + id: plugins + model: UM.PluginsModel { } + } } } diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index fac4fd841d..9551e3bcf5 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -7,15 +7,16 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 import UM 1.1 as UM +import Cura 1.0 as Cura Rectangle { id: base; property bool activity: Printer.getPlatformActivity; property string fileBaseName - property variant activeMachineInstance: UM.MachineManager.activeMachineInstance + property variant activeMachineName: Cura.MachineManager.activeMachineName - onActiveMachineInstanceChanged: + onActiveMachineNameChanged: { base.createFileName() } @@ -28,32 +29,48 @@ Rectangle { height: childrenRect.height color: "transparent" - function createFileName(){ - var splitMachineName = UM.MachineManager.activeMachineInstance.split(" ") - var abbrMachine = '' - for (var i = 0; i < splitMachineName.length; i++){ - if (splitMachineName[i].search(/ultimaker/i) != -1){ - abbrMachine += 'UM' + function createFileName() + { + var splitMachineName = Cura.MachineManager.activeMachineName.split(" ") + var abbrMachine = ""; + if ((UM.Preferences.getValue("cura/jobname_prefix"))) + { + for (var i = 0; i < splitMachineName.length; i++) + { + if (splitMachineName[i].search(/ultimaker/i) != -1) + { + abbrMachine += "UM"; } - else{ + else + { if (splitMachineName[i].charAt(0).search(/[0-9]/g) == -1) - abbrMachine += splitMachineName[i].charAt(0) - } - var regExpAdditives = /[0-9\+]/g; - var resultAdditives = splitMachineName[i].match(regExpAdditives); - if (resultAdditives != null){ - for (var j = 0; j < resultAdditives.length; j++){ - abbrMachine += resultAdditives[j] - + { + abbrMachine += splitMachineName[i].charAt(0); } } } - printJobTextfield.text = abbrMachine + '_' + base.fileBaseName + var regExpAdditives = /[0-9\+]/g; + var resultAdditives = splitMachineName[i].match(regExpAdditives); + if (resultAdditives != null) + { + for (var j = 0; j < resultAdditives.length; j++) + { + abbrMachine += resultAdditives[j]; + } + } + printJobTextfield.text = abbrMachine + "_" + base.fileBaseName; + } + else + { + printJobTextfield.text = base.fileBaseName; + } } - Connections { + Connections + { target: backgroundItem - onHasMesh: { + onHasMesh: + { base.fileBaseName = name } } diff --git a/resources/qml/MachinesPage.qml b/resources/qml/MachinesPage.qml new file mode 100644 index 0000000000..00ebcfc0af --- /dev/null +++ b/resources/qml/MachinesPage.qml @@ -0,0 +1,68 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +UM.ManagementPage +{ + id: base; + + title: catalog.i18nc("@title:tab", "Printers"); + model: UM.ContainerStacksModel + { + filter: {"type": "machine"} + } + + onAddObject: Printer.requestAddPrinter() + onRemoveObject: confirmDialog.open(); + onRenameObject: renameDialog.open(); + onActivateObject: Cura.MachineManager.setActiveMachine(base.currentItem.id) + + removeEnabled: base.currentItem != null && model.rowCount() > 1 + renameEnabled: base.currentItem != null + activateEnabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMachineId + + Flow + { + anchors.fill: parent; + spacing: UM.Theme.getSize("default_margin").height; + + Label + { + text: base.currentItem && base.currentItem.name ? base.currentItem.name : "" + font: UM.Theme.getFont("large") + width: parent.width + elide: Text.ElideRight + } + + Label { text: catalog.i18nc("@label", "Type"); width: parent.width * 0.2; } + Label { text: base.currentItem && base.currentItem.typeName ? base.currentItem.typeName : ""; width: parent.width * 0.7; } + + UM.I18nCatalog { id: catalog; name: "uranium"; } + + UM.ConfirmRemoveDialog + { + id: confirmDialog; + object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; + onYes: Cura.MachineManager.removeMachine(base.currentItem.id); + } + + UM.RenameDialog + { + id: renameDialog; + object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; + onAccepted: + { + Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim()); + //Reselect current item to update details panel + var index = objectList.currentIndex + objectList.currentIndex = -1 + objectList.currentIndex = index + } + } + } +} diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml new file mode 100644 index 0000000000..03ede39a5c --- /dev/null +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -0,0 +1,217 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +UM.ManagementPage +{ + id: base; + + title: catalog.i18nc("@title:tab", "Materials"); + + model: UM.InstanceContainersModel { filter: { "type": "material", "definition": Cura.MachineManager.activeDefinitionId } } +/* + onAddObject: { var selectedMaterial = UM.MaterialManager.createProfile(); base.selectMaterial(selectedMaterial); } + onRemoveObject: confirmDialog.open(); + onRenameObject: { renameDialog.open(); renameDialog.selectText(); } +*/ +// activateEnabled: false + addEnabled: false + removeEnabled: false + renameEnabled: false + + scrollviewCaption: " " + detailsVisible: true + + property string currency: UM.Preferences.getValue("general/currency") + + Item { + UM.I18nCatalog { id: catalog; name: "cura"; } + + visible: base.currentItem != null + anchors.fill: parent + + Label { id: profileName; text: materialProperties.name; font: UM.Theme.getFont("large"); width: parent.width; } + + TabView { + id: scrollView + anchors.left: parent.left + anchors.right: parent.right + anchors.top: profileName.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.bottom: parent.bottom + + Tab { + title: "Information" + anchors.margins: UM.Theme.getSize("default_margin").height + + Flow { + id: containerGrid + + width: scrollView.width; + property real columnWidth: width / 2 + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Profile Type") } + Label { width: parent.columnWidth; text: materialProperties.profile_type } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Supplier") } + Label { width: parent.columnWidth; text: materialProperties.supplier } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Material Type") } + Label { width: parent.columnWidth; text: materialProperties.material_type } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Color") } + + Row { + width: parent.columnWidth; + spacing: UM.Theme.getSize("default_margin").width/2 + Rectangle { + color: materialProperties.color_code + width: colorLabel.height + height: colorLabel.height + border.width: UM.Theme.getSize("default_lining").height + } + Label { id: colorLabel; text: materialProperties.color_name } + } + + Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } + + Label { width: parent.width; text: "" + catalog.i18nc("@label", "Properties") + "" } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Density") } + Label { width: parent.columnWidth; text: materialProperties.density } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Diameter") } + Label { width: parent.columnWidth; text: materialProperties.diameter } + + Label { + text: catalog.i18nc("@label", "Filament cost") + width: parent.columnWidth; + height: spoolCostInput.height + verticalAlignment: Text.AlignVCenter + } + + Row { + width: parent.columnWidth; + Label { + text: base.currency ? base.currency + " " : " " + anchors.verticalCenter: parent.verticalCenter + } + TextField { + id: spoolCostInput + text: materialProperties.spool_cost + } + } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament weight") } + Label { width: parent.columnWidth; text: materialProperties.spool_weight + " " + "g" } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament length") } + Label { width: parent.columnWidth; text: materialProperties.spool_length + " " + "m" } + + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Cost per meter") } + Label { width: parent.columnWidth; text: catalog.i18nc("@label", "approx. %1 %2/m").arg(materialProperties.cost_per_meter).arg(base.currency); } + + Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } + + Label { + text: materialProperties.description ? "" + catalog.i18nc("@label", "Information") + "
" + materialProperties.description : ""; + width: parent.width + wrapMode: Text.WordWrap + } + Label { + text: materialProperties.adhesion_info ? "" + catalog.i18nc("@label", "Adhesion") + "
" + materialProperties.adhesion_info : ""; + width: parent.width + wrapMode: Text.WordWrap + } + } + } + Tab { + title: catalog.i18nc("@label", "Print settings") + anchors.margins: UM.Theme.getSize("default_margin").height + + Grid { + columns: 2 + spacing: UM.Theme.getSize("default_margin").width + + Column { + Repeater { + model: base.currentItem ? base.currentItem.settings : null + Label { + text: modelData.name.toString(); + elide: Text.ElideMiddle; + } + } + } + Column { + Repeater { + model: base.currentItem ? base.currentItem.settings : null + Label { text: modelData.value.toString() + " " + modelData.unit.toString(); } + } + } + } + } + } + + QtObject + { + id: materialProperties + + property string name: "Unknown"; + property string profile_type: "Unknown"; + property string supplier: "Unknown"; + property string material_type: "Unknown"; + + property string color_name: "Yellow"; + property color color_code: "yellow"; + + property string density: "Unknown"; + property string diameter: "Unknown"; + + property string spool_cost: "Unknown"; + property string spool_weight: "Unknown"; + property string spool_length: "Unknown"; + property string cost_per_meter: "Unknown"; + + property string description: ""; + property string adhesion_info: ""; + } + } + + onCurrentItemChanged: + { + if(!currentItem == null) + { + return + } + + materialProperties.name = currentItem.name; + + if(currentItem.metadata != undefined && currentItem.metadata != null) + { + materialProperties.supplier = currentItem.metadata.brand ? currentItem.metadata.brand : "Unknown"; + materialProperties.material_type = currentItem.metadata.material ? currentItem.metadata.material : "Unknown"; + materialProperties.color_name = currentItem.metadata.color_name ? currentItem.metadata.color_name : "Yellow"; + materialProperties.color_code = currentItem.metadata.color_code ? currentItem.metadata.color_code : "yellow"; + + materialProperties.description = currentItem.metadata.description ? currentItem.metadata.description : ""; + materialProperties.adhesion_info = currentItem.metadata.adhesion_info ? currentItem.metadata.adhesion_info : ""; + + if(currentItem.metadata.properties != undefined && currentItem.metadata.properties != null) + { + materialProperties.density = currentItem.metadata.properties.density ? currentItem.metadata.properties.density : "Unknown"; + materialProperties.diameter = currentItem.metadata.properties.diameter ? currentItem.metadata.properties.diameter : "Unknown"; + } + else + { + materialProperties.density = "Unknown"; + materialProperties.diameter = "Unknown"; + } + } + } +} diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml new file mode 100644 index 0000000000..10acc8beef --- /dev/null +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -0,0 +1,225 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +UM.ManagementPage +{ + id: base; + + title: catalog.i18nc("@title:tab", "Profiles"); + addText: catalog.i18nc("@label", "Duplicate") + + model: UM.InstanceContainersModel { filter: { "type": "quality" } } + + onActivateObject: Cura.MachineManager.setActiveQuality(currentItem.id) + onAddObject: { + var selectedProfile; + if (objectList.currentIndex == 0) { + // Current settings + selectedProfile = UM.MachineManager.createProfile(); + } else { + selectedProfile = UM.MachineManager.duplicateProfile(currentItem.name); + } + base.selectProfile(selectedProfile); + + renameDialog.removeWhenRejected = true; + renameDialog.open(); + renameDialog.selectText(); + } + onRemoveObject: confirmDialog.open(); + onRenameObject: { renameDialog.removeWhenRejected = false; renameDialog.open(); renameDialog.selectText(); } + + activateEnabled: currentItem != null ? currentItem.id != Cura.MachineManager.activeQualityId : false; + addEnabled: currentItem != null; + removeEnabled: currentItem != null ? !currentItem.readOnly : false; + renameEnabled: currentItem != null ? !currentItem.readOnly : false; + + scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(UM.MachineManager.activeMachineInstance) + + signal selectProfile(string name) + onSelectProfile: { + objectList.currentIndex = objectList.model.find("name", name); + } + + Item { + visible: base.currentItem != null + anchors.fill: parent + + Label { + id: profileName + text: base.currentItem ? base.currentItem.name : "" + font: UM.Theme.getFont("large") + width: parent.width + elide: Text.ElideRight + } + + ScrollView { + anchors.left: parent.left + anchors.top: profileName.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.right: parent.right + anchors.bottom: parent.bottom + + Column + { + spacing: UM.Theme.getSize("default_margin").height + + Row + { + visible: base.currentItem.id == -1 || currentItem.id == Cura.MachineManager.activeQualityId + Button + { + text: { + var profileName = UM.MachineManager.activeProfile; + profileName = (profileName.length > 20) ? profileName.substring(0, 20) + '...' : profileName; + return catalog.i18nc("@action:button", "Update \"%1\"".arg(profileName)); + } + enabled: UM.ActiveProfile.hasCustomisedValues && !UM.ActiveProfile.readOnly + onClicked: UM.ActiveProfile.updateProfile() + } + + Button + { + text: catalog.i18nc("@action:button", "Discard changes"); + enabled: UM.ActiveProfile.hasCustomisedValues + onClicked: UM.ActiveProfile.discardChanges() + } + } + + Grid + { + id: containerGrid + columns: 2 + spacing: UM.Theme.getSize("default_margin").width + + Label { + text: base.currentItem == null ? "" : + base.currentItem.id == -1 ? catalog.i18nc("@label", "Based on") : catalog.i18nc("@label", "Profile type") + } + Label { + text: base.currentItem == null ? "" : + base.currentItem.id == -1 ? UM.MachineManager.activeProfile : + base.currentItem.readOnly ? catalog.i18nc("@label", "Protected profile") : catalog.i18nc("@label", "Custom profile") + } + + Column { + Repeater { + model: base.currentItem ? base.currentItem.settings : null + Label { + text: modelData.name.toString(); + elide: Text.ElideMiddle; + } + } + } + Column { + Repeater { + model: base.currentItem ? base.currentItem.settings : null + Label { text: modelData.value.toString(); } + } + } + } + } + } + } + + buttons: Row { + + Button + { + text: catalog.i18nc("@action:button", "Import"); + iconName: "document-import"; + onClicked: importDialog.open(); + } + + Button + { + text: catalog.i18nc("@action:button", "Export"); + iconName: "document-export"; + onClicked: exportDialog.open(); + } + } + + Item + { + UM.I18nCatalog { id: catalog; name: "uranium"; } + + UM.ConfirmRemoveDialog + { + id: confirmDialog; + object: base.currentItem != null ? base.currentItem.name : ""; + onYes: base.model.removeProfile(base.currentItem.name); + } + UM.RenameDialog + { + id: renameDialog; + object: base.currentItem != null ? base.currentItem.name : ""; + property bool removeWhenRejected: false; + onAccepted: base.model.renameProfile(base.currentItem.name, newName.trim()); + onRejected: { + if(removeWhenRejected) { + base.model.removeProfile(base.currentItem.name) + } + } + } + MessageDialog + { + id: messageDialog + title: catalog.i18nc("@window:title", "Import Profile"); + standardButtons: StandardButton.Ok + modality: Qt.ApplicationModal + } + + FileDialog + { + id: importDialog; + title: catalog.i18nc("@title:window", "Import Profile"); + selectExisting: true; + nameFilters: base.model.getFileNameFiltersRead() + folder: base.model.getDefaultPath() + onAccepted: + { + var result = base.model.importProfile(fileUrl) + messageDialog.text = result.message + if(result.status == "ok") + { + messageDialog.icon = StandardIcon.Information + } + else if(result.status == "duplicate") + { + messageDialog.icon = StandardIcon.Warning + } + else + { + messageDialog.icon = StandardIcon.Critical + } + messageDialog.open() + } + } + + FileDialog + { + id: exportDialog; + title: catalog.i18nc("@title:window", "Export Profile"); + selectExisting: false; + nameFilters: base.model.getFileNameFiltersWrite() + folder: base.model.getDefaultPath() + onAccepted: + { + var result = base.model.exportProfile(base.currentItem.id, base.currentItem.name, fileUrl, selectedNameFilter) + if(result && result.status == "error") + { + messageDialog.icon = StandardIcon.Critical + messageDialog.text = result.message + messageDialog.open() + } + // else pop-up Message thing from python code + } + } + } +} diff --git a/resources/qml/ProfileSetup.qml b/resources/qml/ProfileSetup.qml index 1e6c3f9996..95aed3685c 100644 --- a/resources/qml/ProfileSetup.qml +++ b/resources/qml/ProfileSetup.qml @@ -6,7 +6,8 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.1 as UM +import UM 1.2 as UM +import Cura 1.0 as Cura Item{ id: base; @@ -14,6 +15,8 @@ Item{ property int totalHeightProfileSetup: childrenRect.height property Action manageProfilesAction property Action addProfileAction + property Action updateProfileAction + property Action resetProfileAction signal showTooltip(Item item, point location, string text) signal hideTooltip() @@ -39,13 +42,13 @@ Item{ property int rightMargin: customisedSettings.visible ? customisedSettings.width + UM.Theme.getSize("default_margin").width / 2 : 0 id: globalProfileSelection - text: UM.MachineManager.activeProfile + text: Cura.MachineManager.activeQualityName width: parent.width/100*55 height: UM.Theme.getSize("setting_control").height anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - tooltip: UM.MachineManager.activeProfile + tooltip: Cura.MachineManager.activeQualityName style: UM.Theme.styles.sidebar_header_button menu: Menu @@ -54,7 +57,10 @@ Item{ Instantiator { id: profileSelectionInstantiator - model: UM.ProfilesModel {} + model: UM.InstanceContainersModel + { + filter: {"type": "quality"} + } property int separatorIndex: -1 Loader { @@ -67,7 +73,7 @@ Item{ //Insert a separator between readonly and custom profiles if(separatorIndex < 0 && index > 0) { if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) { - profileSelectionMenu.addSeparator(); + profileSelectionMenu.insertSeparator(index); separatorIndex = index; } } @@ -96,26 +102,33 @@ Item{ id: item text: model_data ? model_data.name : "" checkable: true; - checked: model_data ? model_data.active : false; + checked: Cura.MachineManager.activeQualityId == model_data.id exclusiveGroup: profileSelectionMenuGroup; onTriggered: { - UM.MachineManager.setActiveProfile(model_data.name); - if (!model_data.active) { + Cura.MachineManager.setActiveQuality(model_data.id); + /*if (!model_data.active) { //Selecting a profile was canceled; undo menu selection profileSelectionInstantiator.model.setProperty(model_index, "active", false); var activeProfileName = UM.MachineManager.activeProfile; var activeProfileIndex = profileSelectionInstantiator.model.find("name", activeProfileName); profileSelectionInstantiator.model.setProperty(activeProfileIndex, "active", true); - } + }*/ } } } MenuSeparator { } + MenuItem { + action: base.updateProfileAction; + } + MenuItem { + action: base.resetProfileAction; + } MenuItem { action: base.addProfileAction; } + MenuSeparator { } MenuItem { action: base.manageProfilesAction; } diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index acdb43d67b..1307e8f820 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -14,21 +14,33 @@ Rectangle { property real progress: UM.Backend.progress; property int backendState: UM.Backend.state; + property bool activity: Printer.getPlatformActivity; //Behavior on progress { NumberAnimation { duration: 250; } } property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property string fileBaseName - property string statusText: { - if(base.backendState == 0) { - if(!activity) { - return catalog.i18nc("@label:PrintjobStatus","Please load a 3d model"); - } else { - return catalog.i18nc("@label:PrintjobStatus","Preparing to slice..."); - } - } else if(base.backendState == 1) { - return catalog.i18nc("@label:PrintjobStatus","Slicing..."); - } else { - return catalog.i18nc("@label:PrintjobStatus","Ready to ") + UM.OutputDeviceManager.activeDeviceShortDescription; + property string statusText: + { + if(!activity) + { + return catalog.i18nc("@label:PrintjobStatus", "Please load a 3d model"); + } + + if(base.backendState == 1) + { + return catalog.i18nc("@label:PrintjobStatus", "Preparing to slice..."); + } + else if(base.backendState == 2) + { + return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); + } + else if(base.backendState == 3) + { + return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); + } + else if(base.backendState == 4) + { + return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") } } @@ -60,7 +72,7 @@ Rectangle { height: parent.height color: UM.Theme.getColor("progressbar_control") radius: UM.Theme.getSize("progressbar_radius").width - visible: base.backendState == 1 ? true : false + visible: base.backendState == 2 ? true : false } } @@ -76,12 +88,12 @@ Rectangle { id: saveToButton tooltip: UM.OutputDeviceManager.activeDeviceDescription; - enabled: base.backendState == 2 && base.activity == true + enabled: base.backendState == 3 && base.activity == true height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: parent.top - anchors.right: deviceSelectionMenu.left; - anchors.rightMargin: -3 * UM.Theme.getSize("default_lining").width; + anchors.right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right + anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("default_margin").width text: UM.OutputDeviceManager.activeDeviceShortDescription onClicked: @@ -127,9 +139,9 @@ Rectangle { anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height - enabled: base.backendState == 2 && base.activity == true + enabled: base.backendState == 3 && base.activity == true + visible: devicesModel.deviceCount > 1 - //iconSource: UM.Theme.icons[UM.OutputDeviceManager.activeDeviceIconName]; style: ButtonStyle { background: Rectangle { diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml new file mode 100644 index 0000000000..f4e3dbe5ae --- /dev/null +++ b/resources/qml/Settings/SettingCategory.qml @@ -0,0 +1,82 @@ +// 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) + + UM.SimpleButton { + id: settingsButton + + visible: base.hovered || settingsButton.hovered + height: base.height * 0.6 + width: base.height * 0.6 + + anchors { + right: inheritButton.visible ? inheritButton.left : parent.right + rightMargin: inheritButton.visible? UM.Theme.getSize("default_margin").width / 2 : UM.Theme.getSize("setting_preferences_button_margin").width + verticalCenter: parent.verticalCenter; + } + + color: UM.Theme.getColor("setting_control_button"); + hoverColor: UM.Theme.getColor("setting_control_button_hover") + iconSource: UM.Theme.getIcon("settings"); + + onClicked: { + Actions.configureSettingVisibility.trigger(definition) + } + } + + UM.SimpleButton + { + id: inheritButton; + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("setting_preferences_button_margin").width + + visible: hiddenValuesCount > 0 + height: parent.height / 2; + width: height; + + onClicked: { + base.showAllHiddenInheritedSettings() + } + + color: UM.Theme.getColor("setting_control_button") + hoverColor: UM.Theme.getColor("setting_control_button_hover") + iconSource: UM.Theme.getIcon("notice") + + onEntered: { + base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible.")) + } + + onExited: { + base.hideTooltip(); + } + + UM.I18nCatalog { id: catalog; name: "cura" } + } +} diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml new file mode 100644 index 0000000000..6f0314160e --- /dev/null +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -0,0 +1,78 @@ +// 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 + +SettingItem +{ + id: base + + contents: MouseArea + { + id: control + anchors.fill: parent + + property bool checked: + { + switch(propertyProvider.properties.value) + { + case "True": + return true + case "False": + return false + default: + return propertyProvider.properties.value + } + } + + onClicked: propertyProvider.setPropertyValue("value", !checked) + + Rectangle + { + anchors + { + top: parent.top + bottom: parent.bottom + left: parent.left + } + width: height + + color: + { + if (!enabled) + { + return UM.Theme.getColor("setting_control_disabled") + } + if(control.containsMouse || control.activeFocus) + { + return UM.Theme.getColor("setting_control_highlight") + } + else + { + return UM.Theme.getColor("setting_control") + } + } + + border.width: UM.Theme.getSize("default_lining").width + border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control.containsMouse ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") + + UM.RecolorImage { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width/2.5 + height: parent.height/2.5 + sourceSize.width: width + sourceSize.height: width + color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text"); + source: UM.Theme.getIcon("check") + opacity: control.checked ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100; } } + } + } + } +} diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml new file mode 100644 index 0000000000..c9f3cb727b --- /dev/null +++ b/resources/qml/Settings/SettingComboBox.qml @@ -0,0 +1,109 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.1 as UM + +SettingItem +{ + id: base + + contents: ComboBox + { + id: control + + model: definition.options + textRole: "value"; + + anchors.fill: parent + + MouseArea + { + anchors.fill: parent; + acceptedButtons: Qt.NoButton; + onWheel: wheel.accepted = true; + } + + style: ComboBoxStyle + { + background: Rectangle + { + color: + { + if (!enabled) + { + return UM.Theme.getColor("setting_control_disabled") + } + if(control.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_control_highlight") + } + else + { + return UM.Theme.getColor("setting_control") + } + } + border.width: UM.Theme.getSize("default_lining").width; + border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border"); + } + label: Item + { + Label + { + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_lining").width + anchors.right: downArrow.left; + anchors.rightMargin: UM.Theme.getSize("default_lining").width; + anchors.verticalCenter: parent.verticalCenter; + + text: control.currentText; + font: UM.Theme.getFont("default"); + color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text"); + + elide: Text.ElideRight; + verticalAlignment: Text.AlignVCenter; + } + + UM.RecolorImage + { + id: downArrow + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2; + anchors.verticalCenter: parent.verticalCenter; + + source: UM.Theme.getIcon("arrow_bottom") + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + 5 + sourceSize.height: width + 5 + + color: UM.Theme.getColor("setting_control_text"); + + } + } + } + + onActivated: provider.setPropertyValue("value", definition.options[index].key) + onModelChanged: updateCurrentIndex(); + + Connections + { + target: provider + onPropertiesChanged: control.updateCurrentIndex() + } + + function updateCurrentIndex() { + for(var i = 0; i < definition.options.length; ++i) { + if(definition.options[i].key == provider.properties.value) { + currentIndex = i; + return; + } + } + + currentIndex = -1; + } + } +} diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml new file mode 100644 index 0000000000..c1666a8157 --- /dev/null +++ b/resources/qml/Settings/SettingItem.qml @@ -0,0 +1,185 @@ +// 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.1 as UM + +import "." + +Item { + id: base; + + height: UM.Theme.getSize("section").height; + + property alias contents: controlContainer.children; + property alias hovered: mouse.containsMouse + + signal contextMenuRequested() + signal showTooltip(string text); + signal hideTooltip(); + + MouseArea + { + id: mouse; + + anchors.fill: parent; + + acceptedButtons: Qt.RightButton; + hoverEnabled: true; + + onClicked: base.contextMenuRequested(); + + onEntered: { + hoverTimer.start(); + } + + onExited: { + if(controlContainer.item && controlContainer.item.hovered) { + return; + } + hoverTimer.stop(); + base.hideTooltip(); + } + + Timer { + id: hoverTimer; + interval: 500; + repeat: false; + + onTriggered: + { + var affects = settingDefinitionsModel.getRequiredBy(definition.key, "value") + var affected_by = settingDefinitionsModel.getRequires(definition.key, "value") + + var affected_by_list = "" + for(var i in affected_by) + { + affected_by_list += "
  • %1
  • \n".arg(affected_by[i].label) + } + + var affects_list = "" + for(var i in affects) + { + affects_list += "
  • %1
  • \n".arg(affects[i].label) + } + + var tooltip = "%1
    \n

    %2

    ".arg(definition.label).arg(definition.description) + + if(affects_list != "") + { + tooltip += "
    %1
    \n".arg(catalog.i18nc("@label", "Affects")).arg(affects_list) + } + + if(affected_by_list != "") + { + tooltip += "
    %1
    \n".arg(catalog.i18nc("@label", "Affected By")).arg(affected_by_list) + } + + base.showTooltip(tooltip); + } + } + + Label + { + id: label; + + anchors.left: parent.left; + anchors.leftMargin: (UM.Theme.getSize("section_icon_column").width + 5) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width) + anchors.right: settingControls.left; + anchors.verticalCenter: parent.verticalCenter + + height: UM.Theme.getSize("section").height; + verticalAlignment: Text.AlignVCenter; + + text: definition.label + elide: Text.ElideMiddle; + + color: UM.Theme.getColor("setting_control_text"); + font: UM.Theme.getFont("default"); + } + + Row + { + id: settingControls + + height: parent.height / 2 + spacing: UM.Theme.getSize("default_margin").width / 2 + + anchors { + right: controlContainer.left + rightMargin: UM.Theme.getSize("default_margin").width / 2 + verticalCenter: parent.verticalCenter + } + + UM.SimpleButton + { + id: revertButton; + + visible: propertyProvider.stackLevel == 0 + + height: parent.height; + width: height; + + backgroundColor: UM.Theme.getColor("setting_control"); + hoverBackgroundColor: UM.Theme.getColor("setting_control_highlight") + color: UM.Theme.getColor("setting_control_button") + hoverColor: UM.Theme.getColor("setting_control_button_hover") + + iconSource: UM.Theme.getIcon("reset") + + onClicked: { + revertButton.focus = true + propertyProvider.removeFromContainer(0) + } + + onEntered: base.showTooltip(catalog.i18nc("@label", "This setting has a value that is different from the profile.\n\nClick to restore the value of the profile.")) + onExited: base.showTooltip(definition.description); + } + + UM.SimpleButton + { + // This button shows when the setting has an inherited function, but is overriden by profile. + id: inheritButton; + + //visible: has_profile_value && base.has_inherit_function && base.is_enabled + visible: propertyProvider.properties.state == "InstanceState.User" && propertyProvider.stackLevel > 0 + + height: parent.height; + width: height; + + onClicked: { + focus = true; + propertyProvider.removeFromContainer(propertyProvider.stackLevel) + } + + backgroundColor: UM.Theme.getColor("setting_control"); + hoverBackgroundColor: UM.Theme.getColor("setting_control_highlight") + color: UM.Theme.getColor("setting_control_button") + hoverColor: UM.Theme.getColor("setting_control_button_hover") + + iconSource: UM.Theme.getIcon("notice"); + + onEntered: base.showTooltip(catalog.i18nc("@label", "This setting is normally calculated, but it currently has an absolute value set.\n\nClick to restore the calculated value.")) + onExited: base.showTooltip(definition.description); + } + + } + + Item + { + id: controlContainer; + + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter; + width: UM.Theme.getSize("setting_control").width; + height: UM.Theme.getSize("setting_control").height + } + } + + UM.I18nCatalog { id: catalog; name: "cura" } +} diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml new file mode 100644 index 0000000000..9972f83aa1 --- /dev/null +++ b/resources/qml/Settings/SettingTextField.qml @@ -0,0 +1,155 @@ +// 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.2 + +import UM 1.1 as UM + +SettingItem +{ + id: base + + contents: Rectangle + { + id: control + + anchors.fill: parent + + property alias hovered: mouseArea.containsMouse; + + border.width: UM.Theme.getSize("default_lining").width + border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") + + property variant parentValue: value //From parent loader + function notifyReset() { + input.text = format(parentValue) + } + + color: { + if (!enabled) + { + return UM.Theme.getColor("setting_control_disabled") + } + switch(propertyProvider.properties.validationState) + { + case "ValidatorState.Exception": + return UM.Theme.getColor("setting_validation_error") + case "ValidatorState.MinimumError": + return UM.Theme.getColor("setting_validation_error") + case "ValidatorState.MaximumError": + return UM.Theme.getColor("setting_validation_error") + case "ValidatorState.MinimumWarning": + return UM.Theme.getColor("setting_validation_warning") + case "ValidatorState.MaximumWarning": + return UM.Theme.getColor("setting_validation_warning") + case "ValidatorState.Valid": + return UM.Theme.getColor("setting_validation_ok") + + default: + return UM.Theme.getColor("setting_control") + } + } + + Rectangle + { + anchors.fill: parent; + anchors.margins: UM.Theme.getSize("default_lining").width; + color: UM.Theme.getColor("setting_control_highlight") + opacity: !control.hovered ? 0 : propertyProvider.properties.validationState == "ValidatorState.Valid" ? 1.0 : 0.35; + } + + Label + { + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.getSize("setting_unit_margin").width + anchors.verticalCenter: parent.verticalCenter; + + text: definition.unit; + color: UM.Theme.getColor("setting_unit") + font: UM.Theme.getFont("default") + } + + MouseArea + { + id: mouseArea + anchors.fill: parent; + hoverEnabled: true; + cursorShape: Qt.IBeamCursor + } + + TextInput + { + id: input + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("setting_unit_margin").width + right: parent.right + verticalCenter: parent.verticalCenter + } + + Keys.onReleased: + { +// text = text.replace(",", ".") // User convenience. We use dots for decimal values +// if(parseFloat(text) != base.parentValue) +// { +// base.valueChanged(parseFloat(text)); +// } + + propertyProvider.setPropertyValue("value", text) + } + + onEditingFinished: + { +// if(parseFloat(text) != base.parentValue) +// { +// base.valueChanged(parseFloat(text)); +// } + propertyProvider.setPropertyValue("value", text) + } + + color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default"); + + selectByMouse: true; + + maximumLength: 10; + + validator: RegExpValidator { regExp: /[0-9.,-]{0,10}/ } + + Binding + { + target: input + property: "text" + value: control.format(propertyProvider.properties.value) + when: !input.activeFocus + } + } + + //Rounds a floating point number to 4 decimals. This prevents floating + //point rounding errors. + // + //input: The number to round. + //decimals: The number of decimals (digits after the radix) to round to. + //return: The rounded number. + function roundFloat(input, decimals) + { + //First convert to fixed-point notation to round the number to 4 decimals and not introduce new floating point errors. + //Then convert to a string (is implicit). The fixed-point notation will be something like "3.200". + //Then remove any trailing zeroes and the radix. + return input.toFixed(decimals).replace(/\.?0*$/, ""); //Match on periods, if any ( \.? ), followed by any number of zeros ( 0* ), then the end of string ( $ ). + } + + //Formats a value for display in the text field. + // + //This correctly handles formatting of float values. + // + //input: The string value to format. + //return: The formatted string. + function format(inputValue) { + return parseFloat(inputValue) ? roundFloat(parseFloat(inputValue), 4) : inputValue //If it's a float, round to four decimals. + } + } +} diff --git a/resources/qml/Settings/SettingUnknown.qml b/resources/qml/Settings/SettingUnknown.qml new file mode 100644 index 0000000000..55e26b6695 --- /dev/null +++ b/resources/qml/Settings/SettingUnknown.qml @@ -0,0 +1,19 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM + +SettingItem +{ + contents: Label + { + anchors.fill: parent + text: value + " " + unit; + color: UM.Theme.getColor("setting_control_text") + + verticalAlignment: Qt.AlignVCenter + } +} diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml new file mode 100644 index 0000000000..560e7b0803 --- /dev/null +++ b/resources/qml/Settings/SettingView.qml @@ -0,0 +1,141 @@ +// 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.2 as UM +import Cura 1.0 as Cura + +import ".." + +ScrollView +{ + id: base; + + style: UM.Theme.styles.scrollview; + flickableItem.flickableDirection: Flickable.VerticalFlick; + + property Action configureSettings; + signal showTooltip(Item item, point location, string text); + signal hideTooltip(); + + ListView + { + id: contents + spacing: UM.Theme.getSize("default_lining").height; + + model: UM.SettingDefinitionsModel { + id: definitionsModel; + containerId: Cura.MachineManager.activeDefinitionId + visibilityHandler: UM.SettingPreferenceVisibilityHandler {} + } + + delegate: Loader + { + id: delegate + + width: UM.Theme.getSize("sidebar").width; + height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : 0 + Behavior on height { NumberAnimation { duration: 100 } } + opacity: provider.properties.enabled == "True" ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + enabled: provider.properties.enabled == "True" + + property var definition: model + property var settingDefinitionsModel: definitionsModel + 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 differnt options. So disable asynchronous loading of enum type completely. + asynchronous: model.type != "enum" + + source: + { + switch(model.type) + { + case "int": + return "SettingTextField.qml" + case "float": + return "SettingTextField.qml" + case "enum": + return "SettingComboBox.qml" + case "bool": + return "SettingCheckBox.qml" + case "str": + return "SettingTextField.qml" + case "category": + return "SettingCategory.qml" + default: + return "SettingUnknown.qml" + } + } + + UM.SettingPropertyProvider + { + id: provider + + containerStackId: Cura.MachineManager.activeMachineId + key: model.key + watchedProperties: [ "value", "enabled", "state", "validationState" ] + storeIndex: 0 + } + + Connections + { + target: item + onContextMenuRequested: { contextMenu.key = model.key; contextMenu.popup() } + onShowTooltip: base.showTooltip(delegate, { x: 0, y: delegate.height / 2 }, text) + onHideTooltip: base.hideTooltip() + } + } + + UM.I18nCatalog { id: catalog; name: "uranium"; } + + add: Transition { + SequentialAnimation { + NumberAnimation { properties: "height"; from: 0; duration: 100 } + NumberAnimation { properties: "opacity"; from: 0; duration: 100 } + } + } + remove: Transition { + SequentialAnimation { + NumberAnimation { properties: "opacity"; to: 0; duration: 100 } + NumberAnimation { properties: "height"; to: 0; duration: 100 } + } + } + addDisplaced: Transition { + NumberAnimation { properties: "x,y"; duration: 100 } + } + removeDisplaced: Transition { + SequentialAnimation { + PauseAnimation { duration: 100; } + NumberAnimation { properties: "x,y"; duration: 100 } + } + } + + Menu + { + id: contextMenu; + + property string key; + + MenuItem + { + //: Settings context menu action + text: catalog.i18nc("@action:menu", "Hide this setting"); + onTriggered: definitionsModel.hide(contextMenu.key); + } + MenuItem + { + //: Settings context menu action + text: catalog.i18nc("@action:menu", "Configure setting visiblity..."); + + onTriggered: Actions.configureSettingVisibility.trigger(contextMenu); + } + } + } +} diff --git a/resources/qml/Settings/SettingsConfigurationPage.qml b/resources/qml/Settings/SettingsConfigurationPage.qml new file mode 100644 index 0000000000..a2889d410a --- /dev/null +++ b/resources/qml/Settings/SettingsConfigurationPage.qml @@ -0,0 +1,119 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQml 2.2 + +import UM 1.1 as UM + +import "../Preferences" + +PreferencesPage +{ + //: Machine configuration page title. + title: catalog.i18nc("@title:tab","Machine"); + id: base + + contents: ColumnLayout + { + z: base.z + anchors.fill: parent; + UM.I18nCatalog { id: catalog; name:"uranium"} + RowLayout + { + //: Active machine combo box label + Label { text: catalog.i18nc("@label:listbox","Active Machine:"); } + ComboBox + { + id: machineCombo; + Layout.fillWidth: true; + model: UM.Models.machinesModel; + textRole: "name"; + onActivated: + { + if(index != -1) + UM.Models.machinesModel.setActive(index); + } + + Connections + { + id: machineChange + target: UM.Application + onMachineChanged: machineCombo.currentIndex = machineCombo.find(UM.Application.machineName); + } + + Component.onCompleted: machineCombo.currentIndex = machineCombo.find(UM.Application.machineName); + } + //: Remove active machine button + Button { text: catalog.i18nc("@action:button","Remove"); onClicked: confirmRemoveDialog.open(); } + } + ScrollView + { + id: settingsScrollView + Layout.fillWidth: true; + Layout.fillHeight: true; + + ListView + { + id: settingsListView + delegate: settingDelegate + model: UM.Models.settingsModel + x: 0 + + section.property: "category" + section.delegate: Label { text: section } + } + } + } + + Component + { + id: settingDelegate + CheckBox + { + z:0 + id: settingCheckBox + text: model.name; + x: depth * 25 + checked: model.visibility + onClicked: ListView.view.model.setVisibility(model.key, checked) + //enabled: !model.disabled + + onHoveredChanged: + { + if(hovered) + { + var xPos = parent.x + settingCheckBox.width; + var yPos = parent.y; + toolTip.show(model.description, 1000, 200, undefined, undefined) //tooltip-text, hover-delay in msec, animation-length in msec, position X, position Y (both y en x == undefined: gives the tooltip a standard placement in the right corner) + } else + { + toolTip.hide(0, 0)//hover-delay in msec, animation-length in msec + } + } + } + } + + PreferencesToolTip + { + id: toolTip; + } + + MessageDialog + { + id: confirmRemoveDialog; + + icon: StandardIcon.Question; + //: Remove machine confirmation dialog title + title: catalog.i18nc("@title:window","Confirm Machine Deletion"); + //: Remove machine confirmation dialog text + text: catalog.i18nc("@label","Are you sure you wish to remove the machine?"); + standardButtons: StandardButton.Yes | StandardButton.No; + + onYes: UM.Models.machinesModel.removeMachine(machineCombo.currentIndex); + } +} diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index f7292cf190..4109fd1586 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -15,6 +15,8 @@ Rectangle property Action addMachineAction; property Action configureMachinesAction; property Action addProfileAction; + property Action updateProfileAction; + property Action resetProfileAction; property Action manageProfilesAction; property Action configureSettingsAction; property int currentModeIndex; @@ -25,7 +27,7 @@ Rectangle function showTooltip(item, position, text) { tooltip.text = text; - position = item.mapToItem(base, position.x, position.y / 2); + position = item.mapToItem(base, position.x, position.y); tooltip.show(position); } @@ -54,20 +56,13 @@ Rectangle configureMachinesAction: base.configureMachinesAction; } - Rectangle { - id: headerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - } - ProfileSetup { id: profileItem addProfileAction: base.addProfileAction + updateProfileAction: base.updateProfileAction + resetProfileAction: base.resetProfileAction manageProfilesAction: base.manageProfilesAction - anchors.top: settingsModeSelection.bottom + anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width height: totalHeightProfileSetup @@ -76,6 +71,15 @@ Rectangle onHideTooltip: base.hideTooltip() } + Rectangle { + id: headerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.top: profileItem.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + } + currentModeIndex: { var index = parseInt(UM.Preferences.getValue("cura/active_mode")) @@ -168,7 +172,7 @@ Rectangle id: sidebarContents anchors.bottom: footerSeparator.top - anchors.top: profileItem.bottom + anchors.top: settingsModeSelection.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: base.left anchors.right: base.right @@ -243,7 +247,6 @@ Rectangle id: sidebarAdvanced; visible: false; - configureSettings: base.configureSettingsAction; onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/SidebarAdvanced.qml index 8a231aa53d..30f4e74db6 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/SidebarAdvanced.qml @@ -5,9 +5,9 @@ import QtQuick 2.0 import QtQuick.Controls 1.2 -import UM 1.0 as UM +import "Settings" -UM.SettingView { - expandedCategories: Printer.expandedCategories; - onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories); +SettingView { +// expandedCategories: Printer.expandedCategories; +// onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories); } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index adc29a3daf..282fa8af25 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -5,7 +5,8 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 -import UM 1.1 as UM +import UM 1.2 as UM +import Cura 1.0 as Cura Item { @@ -15,6 +16,7 @@ Item property Action configureMachinesAction; UM.I18nCatalog { id: catalog; name:"cura"} property int totalHeightHeader: childrenRect.height + property int currentExtruderIndex; Rectangle { id: sidebarTabRow @@ -24,23 +26,11 @@ Item color: UM.Theme.getColor("sidebar_header_bar") } - Label{ - id: printjobTabLabel - text: catalog.i18nc("@label:listbox","Print Job"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width; - anchors.top: sidebarTabRow.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width/100*45 - font: UM.Theme.getFont("large"); - color: UM.Theme.getColor("text") - } - Rectangle { id: machineSelectionRow width: base.width height: UM.Theme.getSize("sidebar_setup").height - anchors.top: printjobTabLabel.bottom + anchors.top: sidebarTabRow.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.horizontalCenter: parent.horizontalCenter @@ -57,10 +47,10 @@ Item ToolButton { id: machineSelection - text: UM.MachineManager.activeMachineInstance; + text: Cura.MachineManager.activeMachineName; width: parent.width/100*55 height: UM.Theme.getSize("setting_control").height - tooltip: UM.MachineManager.activeMachineInstance; + tooltip: Cura.MachineManager.activeMachineName; anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter @@ -71,14 +61,17 @@ Item id: machineSelectionMenu Instantiator { - model: UM.MachineInstancesModel { } + model: UM.ContainerStacksModel + { + filter: {"type": "machine"} + } MenuItem { text: model.name; checkable: true; - checked: model.active; + checked: Cura.MachineManager.activeMachineId == model.id exclusiveGroup: machineSelectionMenuGroup; - onTriggered: UM.MachineManager.setActiveMachineInstance(model.name); + onTriggered: Cura.MachineManager.setActiveMachine(model.id); } onObjectAdded: machineSelectionMenu.insertItem(index, object) onObjectRemoved: machineSelectionMenu.removeItem(object) @@ -95,130 +88,221 @@ Item } Rectangle { - id: variantRow + id: extruderSelection + width: parent.width/100*55 + visible: machineExtruderCount.properties.value > 1 + height: visible ? UM.Theme.getSize("sidebar_header_mode_toggle").height : 0 + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.top: machineSelectionRow.bottom - anchors.topMargin: UM.MachineManager.hasVariants ? UM.Theme.getSize("default_margin").height : 0 - width: base.width - height: UM.MachineManager.hasVariants ? UM.Theme.getSize("sidebar_setup").height : 0 - visible: UM.MachineManager.hasVariants + anchors.topMargin: visible ? UM.Theme.getSize("default_margin").height : 0 + Component{ + id: wizardDelegate + Button { + height: extruderSelection.height + anchors.left: parent.left + anchors.leftMargin: model.index * (extruderSelection.width / machineExtruderCount.properties.value) + anchors.verticalCenter: parent.verticalCenter + width: parent.width / machineExtruderCount.properties.value + text: model.text + exclusiveGroup: extruderMenuGroup; + checkable: true; + checked: base.currentExtruderIndex == index + onClicked: base.currentExtruderIndex = index - Label{ - id: variantLabel - text: catalog.i18nc("@label","Nozzle:"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width; - anchors.verticalCenter: parent.verticalCenter - width: parent.width/100*45 - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton { - id: variantSelection - text: UM.MachineManager.activeMachineVariant - width: parent.width/100*55 - height: UM.Theme.getSize("setting_control").height - tooltip: UM.MachineManager.activeMachineVariant; - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: parent.verticalCenter - style: UM.Theme.styles.sidebar_header_button - - menu: Menu - { - id: variantsSelectionMenu - Instantiator - { - id: variantSelectionInstantiator - model: UM.MachineVariantsModel { id: variantsModel } - MenuItem - { - text: model.name; - checkable: true; - checked: model.active; - exclusiveGroup: variantSelectionMenuGroup; - onTriggered: - { - UM.MachineManager.setActiveMachineVariant(variantsModel.getItem(index).name); - if (typeof(model) !== "undefined" && !model.active) { - //Selecting a variant was canceled; undo menu selection - variantSelectionInstantiator.model.setProperty(index, "active", false); - var activeMachineVariantName = UM.MachineManager.activeMachineVariant; - var activeMachineVariantIndex = variantSelectionInstantiator.model.find("name", activeMachineVariantName); - variantSelectionInstantiator.model.setProperty(activeMachineVariantIndex, "active", true); - } + style: ButtonStyle { + background: Rectangle { + border.width: UM.Theme.getSize("default_lining").width + border.color: control.checked ? UM.Theme.getColor("toggle_checked_border") : + control.pressed ? UM.Theme.getColor("toggle_active_border") : + control.hovered ? UM.Theme.getColor("toggle_hovered_border") : UM.Theme.getColor("toggle_unchecked_border") + color: control.checked ? UM.Theme.getColor("toggle_checked") : + control.pressed ? UM.Theme.getColor("toggle_active") : + control.hovered ? UM.Theme.getColor("toggle_hovered") : UM.Theme.getColor("toggle_unchecked") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.centerIn: parent + color: control.checked ? UM.Theme.getColor("toggle_checked_text") : + control.pressed ? UM.Theme.getColor("toggle_active_text") : + control.hovered ? UM.Theme.getColor("toggle_hovered_text") : UM.Theme.getColor("toggle_unchecked_text") + font: UM.Theme.getFont("default") + text: control.text; } } - onObjectAdded: variantsSelectionMenu.insertItem(index, object) - onObjectRemoved: variantsSelectionMenu.removeItem(object) + label: Item { } } - - ExclusiveGroup { id: variantSelectionMenuGroup; } } } + ExclusiveGroup { id: extruderMenuGroup; } + ListView{ + id: extrudersList + property var index: 0 + model: extrudersListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + } + } + + ListModel + { + id: extrudersListModel + Component.onCompleted: populateExtruderModel() + } + Connections + { + id: machineChange + target: Cura.MachineManager + onGlobalContainerChanged: populateExtruderModel() + } + + function populateExtruderModel() + { + extrudersListModel.clear(); + for(var extruder = 0; extruder < machineExtruderCount.properties.value ; extruder++) { + extrudersListModel.append({ + text: catalog.i18nc("@label", "Extruder %1").arg(extruder + 1) + }) + } } Rectangle { - id: materialSelectionRow - anchors.top: variantRow.bottom - anchors.topMargin: UM.MachineManager.hasMaterials ? UM.Theme.getSize("default_margin").height : 0 + id: variantRow + anchors.top: extruderSelection.visible ? extruderSelection.bottom : machineSelectionRow.bottom + anchors.topMargin: visible ? UM.Theme.getSize("default_margin").height : 0 width: base.width - height: UM.MachineManager.hasMaterials ? UM.Theme.getSize("sidebar_setup").height : 0 - visible: UM.MachineManager.hasMaterials + height: visible ? UM.Theme.getSize("sidebar_setup").height : 0 + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials Label{ - id: materialSelectionLabel - text: catalog.i18nc("@label","Material:"); + id: variantLabel + text: (Cura.MachineManager.hasVariants && Cura.MachineManager.hasMaterials) ? catalog.i18nc("@label","Nozzle & Material:"): + Cura.MachineManager.hasVariants ? catalog.i18nc("@label","Nozzle:") : catalog.i18nc("@label","Material:"); anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width; + anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter width: parent.width/100*45 font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } - ToolButton { - id: materialSelection - text: UM.MachineManager.activeMaterial - width: parent.width/100*55 - height: UM.Theme.getSize("setting_control").height - tooltip: UM.MachineManager.activeMaterial; + Rectangle + { anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - style: UM.Theme.styles.sidebar_header_button - menu: Menu - { - id: materialSelectionMenu - Instantiator + width: parent.width/100*55 + height: UM.Theme.getSize("setting_control").height + + ToolButton { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: materialSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width + anchors.left: parent.left + style: UM.Theme.styles.sidebar_header_button + + menu: Menu { - id: materialSelectionInstantiator - model: UM.MachineMaterialsModel { id: machineMaterialsModel } - MenuItem + id: variantsSelectionMenu + Instantiator { - text: model.name; - checkable: true; - checked: model.active; - exclusiveGroup: materialSelectionMenuGroup; - onTriggered: + id: variantSelectionInstantiator + model: UM.InstanceContainersModel { - UM.MachineManager.setActiveMaterial(machineMaterialsModel.getItem(index).name); - if (typeof(model) !== "undefined" && !model.active) { - //Selecting a material was canceled; undo menu selection - materialSelectionInstantiator.model.setProperty(index, "active", false); - var activeMaterialName = UM.MachineManager.activeMaterial; - var activeMaterialIndex = materialSelectionInstantiator.model.find("name", activeMaterialName); - materialSelectionInstantiator.model.setProperty(activeMaterialIndex, "active", true); + filter: + { + "type": "variant", + "definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine } } + MenuItem + { + text: model.name; + checkable: true; + checked: model.id == Cura.MachineManager.activeVariantId; + exclusiveGroup: variantSelectionMenuGroup; + onTriggered: + { + Cura.MachineManager.setActiveVariant(model.id); + /*if (typeof(model) !== "undefined" && !model.active) { + //Selecting a variant was canceled; undo menu selection + variantSelectionInstantiator.model.setProperty(index, "active", false); + var activeMachineVariantName = UM.MachineManager.activeMachineVariant; + var activeMachineVariantIndex = variantSelectionInstantiator.model.find("name", activeMachineVariantName); + variantSelectionInstantiator.model.setProperty(activeMachineVariantIndex, "active", true); + }*/ + } + } + onObjectAdded: variantsSelectionMenu.insertItem(index, object) + onObjectRemoved: variantsSelectionMenu.removeItem(object) } - onObjectAdded: materialSelectionMenu.insertItem(index, object) - onObjectRemoved: materialSelectionMenu.removeItem(object) - } - ExclusiveGroup { id: materialSelectionMenuGroup; } + ExclusiveGroup { id: variantSelectionMenuGroup; } + } + } + + ToolButton { + id: materialSelection + text: Cura.MachineManager.activeMaterialName + tooltip: Cura.MachineManager.activeMaterialName + visible: Cura.MachineManager.hasMaterials + + height: UM.Theme.getSize("setting_control").height + width: variantSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + + menu: Menu + { + id: materialSelectionMenu + Instantiator + { + id: materialSelectionInstantiator + model: UM.InstanceContainersModel + { + filter: { "type": "material", "definition": Cura.MachineManager.activeDefinitionId } + } + MenuItem + { + text: model.name; + checkable: true; + checked: model.id == Cura.MachineManager.activeMaterialId; + exclusiveGroup: materialSelectionMenuGroup; + onTriggered: + { + Cura.MachineManager.setActiveMaterial(model.id); + /*if (typeof(model) !== "undefined" && !model.active) { + //Selecting a material was canceled; undo menu selection + materialSelectionInstantiator.model.setProperty(index, "active", false); + var activeMaterialName = Cura.MachineManager.activeMaterialName + var activeMaterialIndex = materialSelectionInstantiator.model.find("name", activeMaterialName); + materialSelectionInstantiator.model.setProperty(activeMaterialIndex, "active", true); + }*/ + } + } + onObjectAdded: materialSelectionMenu.insertItem(index, object) + onObjectRemoved: materialSelectionMenu.removeItem(object) + } + + ExclusiveGroup { id: materialSelectionMenuGroup; } + } } } } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } } diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index c3d2db9f3d..c828b5d3db 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -6,7 +6,8 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.1 as UM +import UM 1.2 as UM +import Cura 1.0 as Cura Item { @@ -56,12 +57,7 @@ Item Repeater { id: infillListView property int activeIndex: { - if(!UM.ActiveProfile.valid) - { - return -1; - } - - var density = parseInt(UM.ActiveProfile.settingValues.getValue("infill_sparse_density")); + var density = parseInt(infillDensity.properties.value) for(var i = 0; i < infillModel.count; ++i) { if(density > infillModel.get(i).percentageMin && density <= infillModel.get(i).percentageMax ) @@ -116,11 +112,11 @@ Item onClicked: { if (infillListView.activeIndex != index) { - UM.MachineManager.setSettingValue("infill_sparse_density", model.percentage) + infillDensity.setPropertyValue("value", model.percentage) } } onEntered: { - base.showTooltip(infillCellRight, Qt.point(-infillCellRight.x, parent.height), model.text); + base.showTooltip(infillCellRight, Qt.point(-infillCellRight.x, 0), model.text); } onExited: { base.hideTooltip(); @@ -179,52 +175,50 @@ Item } Rectangle { - id: helpersCellLeft + id: helpersCell anchors.top: infillCellRight.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left - width: parent.width/100*35 - UM.Theme.getSize("default_margin").width + anchors.right: parent.right height: childrenRect.height Label{ + id: adhesionHelperLabel anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - //: Helpers selection label - text: catalog.i18nc("@label:listbox","Helpers:"); + anchors.verticalCenter: brimCheckBox.verticalCenter + width: parent.width/100*35 - 3 * UM.Theme.getSize("default_margin").width + //: Bed adhesion label + text: catalog.i18nc("@label:listbox","Bed Adhesion:"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } - } - Rectangle { - id: helpersCellRight - anchors.top: helpersCellLeft.top - anchors.left: helpersCellLeft.right - width: parent.width/100*65 - UM.Theme.getSize("default_margin").width - height: childrenRect.height CheckBox{ id: brimCheckBox property bool hovered_ex: false anchors.top: parent.top - anchors.left: parent.left + anchors.left: adhesionHelperLabel.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width //: Setting enable skirt adhesion checkbox - text: catalog.i18nc("@option:check","Generate Brim"); + text: catalog.i18nc("@option:check","Print Brim"); style: UM.Theme.styles.checkbox; - checked: UM.ActiveProfile.valid ? UM.ActiveProfile.settingValues.getValue("adhesion_type") == "brim" : false; + checked: platformAdhesionType.properties.value == "brim" + MouseArea { anchors.fill: parent hoverEnabled: true onClicked: { - UM.MachineManager.setSettingValue("adhesion_type", !parent.checked?"brim":"skirt") + platformAdhesionType.setPropertyValue("value", !parent.checked ? "brim" : "skirt") } onEntered: { parent.hovered_ex = true - base.showTooltip(brimCheckBox, Qt.point(-helpersCellRight.x, parent.height), + base.showTooltip(brimCheckBox, Qt.point(-brimCheckBox.x, 0), catalog.i18nc("@label", "Enable printing a brim. This will add a single-layer-thick flat area around your object which is easy to cut off afterwards.")); } onExited: @@ -234,30 +228,45 @@ Item } } } + + Label{ + id: supportHelperLabel + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: supportCheckBox.verticalCenter + width: parent.width/100*35 - 3 * UM.Theme.getSize("default_margin").width + //: Support label + text: catalog.i18nc("@label:listbox","Support:"); + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + CheckBox{ id: supportCheckBox + visible: machineExtruderCount.properties.value <= 1 property bool hovered_ex: false anchors.top: brimCheckBox.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.left: parent.left + anchors.left: supportHelperLabel.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width //: Setting enable support checkbox - text: catalog.i18nc("@option:check","Generate Support Structure"); + text: catalog.i18nc("@option:check","Print Support Structure"); style: UM.Theme.styles.checkbox; - checked: UM.ActiveProfile.valid ? UM.ActiveProfile.settingValues.getValue("support_enable") : false; + checked: supportEnabled.properties.value == "True" MouseArea { anchors.fill: parent hoverEnabled: true onClicked: { - UM.MachineManager.setSettingValue("support_enable", !parent.checked) + supportEnabled.setPropertyValue("value", !parent.checked) } onEntered: { parent.hovered_ex = true - base.showTooltip(supportCheckBox, Qt.point(-helpersCellRight.x, parent.height), + base.showTooltip(supportCheckBox, Qt.point(-supportCheckBox.x, 0), catalog.i18nc("@label", "Enable printing support structures. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); } onExited: @@ -267,11 +276,76 @@ Item } } } + + ComboBox { + id: supportExtruderCombobox + visible: machineExtruderCount.properties.value > 1 + model: extruderModel + + anchors.top: brimCheckBox.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.left: supportHelperLabel.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + width: parent.width/100*45 + + style: UM.Theme.styles.combobox + property bool hovered_ex: false + + currentIndex: supportEnabled.properties.value == "True" ? parseFloat(supportExtruderNr.properties.value) + 1 : 0 + onActivated: { + if(index==0) { + supportEnabled.setPropertyValue("value", false); + } else { + supportEnabled.setPropertyValue("value", true); + supportExtruderNr.setPropertyValue("value", index - 1); + } + } + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: + { + parent.hovered_ex = true + base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), + catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + } + onExited: + { + parent.hovered_ex = false + base.hideTooltip(); + } + } + } + + ListModel { + id: extruderModel + Component.onCompleted: populateExtruderModel() + } + Connections + { + id: machineChange + target: Cura.MachineManager + onGlobalContainerChanged: populateExtruderModel() + } + } + + function populateExtruderModel() + { + extruderModel.clear(); + extruderModel.append({ + text: catalog.i18nc("@label", "Don't print support") + }) + for(var extruder = 0; extruder < machineExtruderCount.properties.value ; extruder++) { + extruderModel.append({ + text: catalog.i18nc("@label", "Print using Extruder %1").arg(extruder + 1) + }) + } } Rectangle { id: tipsCell - anchors.top: helpersCellRight.bottom + anchors.top: helpersCell.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left width: parent.width @@ -291,4 +365,55 @@ Item onLinkActivated: Qt.openUrlExternally(link) } } + + UM.SettingPropertyProvider + { + id: infillDensity + + containerStackId: Cura.MachineManager.activeMachineId + key: "infill_sparse_density" + watchedProperties: [ "value" ] + storeIndex: 0 + + onPropertiesChanged: console.log(properties.value) + } + + UM.SettingPropertyProvider + { + id: platformAdhesionType + + containerStackId: Cura.MachineManager.activeMachineId + key: "adhesion_type" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: supportEnabled + + containerStackId: Cura.MachineManager.activeMachineId + key: "support_enable" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + UM.SettingPropertyProvider + { + id: supportExtruderNr + + containerStackId: Cura.MachineManager.activeMachineId + key: "support_extruder_nr" + watchedProperties: [ "value" ] + storeIndex: 0 + } } diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/SidebarTooltip.qml index 1c7f4bcb76..5cb7ff1f0b 100644 --- a/resources/qml/SidebarTooltip.qml +++ b/resources/qml/SidebarTooltip.qml @@ -31,7 +31,7 @@ UM.PointingRectangle { y = position.y - UM.Theme.getSize("tooltip_arrow_margins").height; } base.opacity = 1; - target = Qt.point(40 , position.y) + target = Qt.point(40 , position.y + UM.Theme.getSize("tooltip_arrow_margins").height / 2) } function hide() { diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index d5274cd873..bde063de10 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -25,7 +25,7 @@ Item { Repeater { id: repeat - model: UM.Models.toolModel + model: UM.ToolModel { } Button { text: model.name diff --git a/resources/qml/WizardPages/AddMachine.qml b/resources/qml/WizardPages/AddMachine.qml index 0c235320db..e4d40d7723 100644 --- a/resources/qml/WizardPages/AddMachine.qml +++ b/resources/qml/WizardPages/AddMachine.qml @@ -34,25 +34,10 @@ Item target: base.wizard onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element { - var name = machineName.text - - var old_page_count = base.wizard.getPageCount() - // Delete old pages (if any) - for (var i = old_page_count - 1; i > 0; i--) - { - base.wizard.removePage(i) - } + base.wizard.resetPages() saveMachine() } - onBackClicked: - { - var old_page_count = base.wizard.getPageCount() - // Delete old pages (if any) - for (var i = old_page_count - 1; i > 0; i--) - { - base.wizard.removePage(i) - } - } + onBackClicked: base.wizard.resetPages() } Label @@ -249,9 +234,6 @@ Item break; } } - if(base.wizard.lastPage == true){ - base.wizard.visible = false - } } } diff --git a/resources/qml/WizardPages/Bedleveling.qml b/resources/qml/WizardPages/Bedleveling.qml index 1721f0fd4a..1a7b85a07b 100644 --- a/resources/qml/WizardPages/Bedleveling.qml +++ b/resources/qml/WizardPages/Bedleveling.qml @@ -18,27 +18,16 @@ Item property int platform_width: UM.MachineManager.getSettingValue("machine_width") property int platform_height: UM.MachineManager.getSettingValue("machine_depth") anchors.fill: parent; - property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.getItem(0).printer + property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer Component.onCompleted: { printer_connection.homeBed() - printer_connection.moveHeadRelative(0, 0, 3) + printer_connection.moveHead(0, 0, 3) printer_connection.homeHead() } UM.I18nCatalog { id: catalog; name:"cura"} property variant wizard: null; - Connections - { - target: wizardPage.wizard - onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element - { - if(wizardPage.wizard.lastPage == true){ - wizardPage.wizard.visible = false - } - } - } - Label { id: pageTitle @@ -84,23 +73,23 @@ Item { if(wizardPage.leveling_state == 0) { - printer_connection.moveHeadRelative(0, 0, 3) + printer_connection.moveHead(0, 0, 3) printer_connection.homeHead() - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(platform_width - 10, 0, 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(platform_width - 10, 0, 0) + printer_connection.moveHead(0, 0, -3) } if(wizardPage.leveling_state == 1) { - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(-platform_width/2, platform_height - 10, 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(-platform_width/2, platform_height - 10, 0) + printer_connection.moveHead(0, 0, -3) } if(wizardPage.leveling_state == 2) { - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(-platform_width/2 + 10, -(platform_height + 10), 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(-platform_width/2 + 10, -(platform_height + 10), 0) + printer_connection.moveHead(0, 0, -3) } wizardPage.leveling_state++ if (wizardPage.leveling_state >= 3){ @@ -120,18 +109,7 @@ Item anchors.left: parent.width < wizardPage.width ? bedlevelingButton.right : parent.left anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0 text: catalog.i18nc("@action:button","Skip Bedleveling"); - onClicked: { - if(wizardPage.wizard.lastPage == true){ - var old_page_count = wizardPage.wizard.getPageCount() - // Delete old pages (if any) - for (var i = old_page_count - 1; i > 0; i--) - { - wizardPage.wizard.removePage(i) - } - wizardPage.wizard.currentPage = 0 - wizardPage.wizard.visible = false - } - } + onClicked: base.nextPage() } } diff --git a/resources/qml/WizardPages/SelectUpgradedParts.qml b/resources/qml/WizardPages/SelectUpgradedParts.qml index 4a327a6ed4..a49401ada9 100644 --- a/resources/qml/WizardPages/SelectUpgradedParts.qml +++ b/resources/qml/WizardPages/SelectUpgradedParts.qml @@ -17,9 +17,6 @@ Item Component.onDestruction: { - if (extruderCheckBox.checked == true){ - UM.MachineManager.setMachineSettingValue("machine_extruder_drive_upgrade", true) - } if (heatedBedCheckBox1.checked == true || heatedBedCheckBox2.checked == true){ UM.MachineManager.setMachineSettingValue("machine_heated_bed", true) } @@ -52,12 +49,6 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width - UM.Theme.getSize("default_margin").width CheckBox - { - id: extruderCheckBox - text: catalog.i18nc("@option:check","Extruder driver ugrades") - checked: true - } - CheckBox { id: heatedBedCheckBox1 text: catalog.i18nc("@option:check","Heated printer bed") diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index b8c8aebe12..a57174a53d 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -31,9 +31,9 @@ Item } property variant printer_connection: { - if (UM.USBPrinterManager.connectedPrinterList.rowCount() != 0){ + if (Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0){ wizardPage.checkupProgress.connection = true - return UM.USBPrinterManager.connectedPrinterList.getItem(0).printer + return Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer } else { return null @@ -114,9 +114,7 @@ Item anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0 //enabled: !alreadyTested text: catalog.i18nc("@action:button","Skip Printer Check"); - onClicked: { - base.currentPage += 1 - } + onClicked: base.nextPage() } } @@ -142,7 +140,7 @@ Item anchors.left: connectionLabel.right anchors.top: parent.top wrapMode: Text.WordWrap - text: UM.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete") + text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete") } ////////////////////////////////////////////////////////// Label @@ -239,7 +237,7 @@ Item if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupNozzle(190) + printer_connection.setTargetHotendTemperature(0, 190) wizardPage.extruder_target_temp = 190 } } @@ -253,7 +251,7 @@ Item anchors.leftMargin: UM.Theme.getSize("default_margin").width width: wizardPage.rightRow * 0.2 wrapMode: Text.WordWrap - text: printer_connection != null ? printer_connection.extruderTemperature + "°C" : "0°C" + text: printer_connection != null ? printer_connection.hotendTemperatures[0] + "°C" : "0°C" font.bold: true } ///////////////////////////////////////////////////////////////////////////// @@ -295,7 +293,7 @@ Item if(printer_connection != null) { bedTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupBed(60) + printer_connection.setTargetBedTemperature(60) wizardPage.bed_target_temp = 60 } } @@ -348,16 +346,16 @@ Item } } - onExtruderTemperatureChanged: + onHotendTemperaturesChanged: { - if(printer_connection.extruderTemperature > wizardPage.extruder_target_temp - 10 && printer_connection.extruderTemperature < wizardPage.extruder_target_temp + 10) + if(printer_connection.hotendTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.hotendTemperatures[0] < wizardPage.extruder_target_temp + 10) { if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:status","Works") wizardPage.checkupProgress.nozzleTemp = true checkTotalCheckUp() - printer_connection.heatupNozzle(0) + printer_connection.setTargetHotendTemperature(0, 0) } } } @@ -368,7 +366,7 @@ Item bedTempStatus.text = catalog.i18nc("@info:status","Works") wizardPage.checkupProgress.bedTemp = true checkTotalCheckUp() - printer_connection.heatupBed(0) + printer_connection.setTargetBedTemperature(0) } } } diff --git a/resources/qml/WizardPages/UpgradeFirmware.qml b/resources/qml/WizardPages/UpgradeFirmware.qml index 4bbb049f20..18bad132c6 100644 --- a/resources/qml/WizardPages/UpgradeFirmware.qml +++ b/resources/qml/WizardPages/UpgradeFirmware.qml @@ -14,7 +14,7 @@ Item SystemPalette{id: palette} UM.I18nCatalog { id: catalog; name:"cura"} - property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? UM.USBPrinterManager.connectedPrinterList.getItem(0).printer : null + property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer : null Label { id: pageTitle @@ -62,7 +62,7 @@ Item anchors.top: parent.top anchors.left: parent.left text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware"); - onClicked: UM.USBPrinterManager.updateAllFirmware() + onClicked: Cura.USBPrinterManager.updateAllFirmware() } Button { id: skipUpgradeButton @@ -71,9 +71,7 @@ Item anchors.left: parent.width < wizardPage.width ? upgradeButton.right : parent.left anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0 text: catalog.i18nc("@action:button","Skip Upgrade"); - onClicked: { - base.currentPage += 1 - } + onClicked: base.nextPage() } } ExclusiveGroup { id: printerGroup; } diff --git a/resources/qml/qmldir b/resources/qml/qmldir new file mode 100644 index 0000000000..096561aca5 --- /dev/null +++ b/resources/qml/qmldir @@ -0,0 +1,3 @@ +module Cura + +singleton Actions 1.0 Actions.qml diff --git a/resources/quality/high.inst.cfg b/resources/quality/high.inst.cfg new file mode 100644 index 0000000000..0329e9ffe1 --- /dev/null +++ b/resources/quality/high.inst.cfg @@ -0,0 +1,10 @@ +[general] +version = 2 +name = High Quality +definition = fdmprinter + +[metadata] +type = quality + +[values] +layer_height = 0.06 diff --git a/resources/quality/normal.inst.cfg b/resources/quality/normal.inst.cfg new file mode 100644 index 0000000000..6d317cdf7a --- /dev/null +++ b/resources/quality/normal.inst.cfg @@ -0,0 +1,9 @@ +[general] +version = 2 +name = Normal Quality +definition = fdmprinter + +[metadata] +type = quality + +[values] diff --git a/resources/themes/cura/icons/notice.svg b/resources/themes/cura/icons/notice.svg index 92790ad8ac..67595b326c 100644 --- a/resources/themes/cura/icons/notice.svg +++ b/resources/themes/cura/icons/notice.svg @@ -2,16 +2,12 @@ - + width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"> + + diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 5c88645330..b2c2329169 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -280,33 +280,50 @@ QtObject { } } - property variant setting_item: UM.SettingItemStyle { - labelFont: Theme.getFont("default"); - labelColor: Theme.getColor("setting_control_text"); + property Component combobox: Component { + ComboBoxStyle { + background: Rectangle { + implicitHeight: UM.Theme.getSize("setting_control").height; + implicitWidth: UM.Theme.getSize("setting_control").width; - spacing: Theme.getSize("default_lining").height; - fixedHeight: Theme.getSize("setting").height; + color: (control.hovered || control.hovered_ex) ? Theme.getColor("setting_control_highlight") : Theme.getColor("setting_control"); + Behavior on color { ColorAnimation { duration: 50; } } - controlWidth: Theme.getSize("setting_control").width; - controlRightMargin: Theme.getSize("setting_control_margin").width; - controlColor: Theme.getColor("setting_control"); - controlHighlightColor: Theme.getColor("setting_control_highlight"); - controlBorderColor: Theme.getColor("setting_control_border"); - controlBorderHighlightColor: Theme.getColor("setting_control_border_highlight"); - controlTextColor: Theme.getColor("setting_control_text"); - controlBorderWidth: Theme.getSize("default_lining").width; - controlDisabledColor: Theme.getColor("setting_control_disabled"); - controlDisabledTextColor: Theme.getColor("setting_control_disabled_text"); - controlDisabledBorderColor: Theme.getColor("setting_control_disabled_border"); - controlFont: Theme.getFont("default"); + border.width: Theme.getSize("default_lining").width; + border.color: (control.hovered || control.hovered_ex) ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border"); + } + label: Item { + Label { + anchors.left: parent.left; + anchors.leftMargin: Theme.getSize("default_lining").width + anchors.right: downArrow.left; + anchors.rightMargin: Theme.getSize("default_lining").width; + anchors.verticalCenter: parent.verticalCenter; - validationErrorColor: Theme.getColor("setting_validation_error"); - validationWarningColor: Theme.getColor("setting_validation_warning"); - validationOkColor: Theme.getColor("setting_validation_ok"); + text: control.currentText; + font: UM.Theme.getFont("default"); + color: !enabled ? Theme.getColor("setting_control_disabled_text") : Theme.getColor("setting_control_text"); - unitRightMargin: Theme.getSize("setting_unit_margin").width; - unitColor: Theme.getColor("setting_unit"); - unitFont: Theme.getFont("default"); + elide: Text.ElideRight; + verticalAlignment: Text.AlignVCenter; + } + + UM.RecolorImage { + id: downArrow + anchors.right: parent.right; + anchors.rightMargin: Theme.getSize("default_lining").width * 2; + anchors.verticalCenter: parent.verticalCenter; + + source: UM.Theme.getIcon("arrow_bottom") + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + 5 + sourceSize.height: width + 5 + + color: Theme.getColor("setting_control_text"); + } + } + } } property Component checkbox: Component { diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index df678e83c9..acd60e2646 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -176,7 +176,7 @@ "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], - "setting": [19.0, 1.8], + "setting": [25.0, 1.8], "setting_control": [10.0, 2.0], "setting_control_depth_margin": [1.4, 0.0], "setting_preferences_button_margin": [3.3, 0.0], @@ -189,7 +189,7 @@ "standard_arrow": [0.8, 0.8], "button": [4, 4], - "button_icon": [3, 3], + "button_icon": [2.5, 2.5], "button_lining": [0, 0], "button_tooltip": [1.0, 1.3], diff --git a/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg new file mode 100644 index 0000000000..b499db6163 --- /dev/null +++ b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg @@ -0,0 +1,17 @@ +[general] +name = 0.25 mm +version = 2 +definition = ultimaker2_extended_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.25 +machine_nozzle_tip_outer_diameter = 0.8 +coasting_volume = 0.1 +coasting_min_volume = 0.17 +speed_wall = =round(speed_print / 1.2, 1) +speed_wall_0 = =1 if speed_wall < 5 else (speed_wall - 5) +speed_topbottom = =round(speed_print / 1.5, 1) diff --git a/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg new file mode 100644 index 0000000000..d2fb6f76b1 --- /dev/null +++ b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg @@ -0,0 +1,15 @@ +[general] +name = 0.4 mm +version = 2 +definition = ultimaker2_extended_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.4 +machine_nozzle_tip_outer_diameter = 1.05 +speed_wall = =round(speed_print / 1.25, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2.25, 1) diff --git a/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg new file mode 100644 index 0000000000..e4f9f0ce45 --- /dev/null +++ b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg @@ -0,0 +1,16 @@ +[general] +name = 0.6 mm +version = 2 +definition = ultimaker2_extended_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.6 +machine_nozzle_tip_outer_diameter = 1.25 +coasting_volume = 1.36 +speed_wall = =round(speed_print * 4 / 3, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2, 1) diff --git a/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg new file mode 100644 index 0000000000..18570ea75d --- /dev/null +++ b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg @@ -0,0 +1,16 @@ +[general] +name = 0.8 mm +version = 2 +definition = ultimaker2_extended_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.8 +machine_nozzle_tip_outer_diameter = 1.35 +coasting_volume = 3.22 +speed_wall = =round(speed_print * 4 / 3, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2, 1) diff --git a/resources/variants/ultimaker2_plus_0.25.inst.cfg b/resources/variants/ultimaker2_plus_0.25.inst.cfg new file mode 100644 index 0000000000..7cab771101 --- /dev/null +++ b/resources/variants/ultimaker2_plus_0.25.inst.cfg @@ -0,0 +1,17 @@ +[general] +name = 0.25 mm +version = 2 +definition = ultimaker2_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.25 +machine_nozzle_tip_outer_diameter = 0.8 +coasting_volume = 0.1 +coasting_min_volume = 0.17 +speed_wall = =round(speed_print / 1.2, 1) +speed_wall_0 = =1 if speed_wall < 5 else (speed_wall - 5) +speed_topbottom = =round(speed_print / 1.5, 1) diff --git a/resources/variants/ultimaker2_plus_0.4.inst.cfg b/resources/variants/ultimaker2_plus_0.4.inst.cfg new file mode 100644 index 0000000000..748f367250 --- /dev/null +++ b/resources/variants/ultimaker2_plus_0.4.inst.cfg @@ -0,0 +1,15 @@ +[general] +name = 0.4 mm +version = 2 +definition = ultimaker2_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.4 +machine_nozzle_tip_outer_diameter = 1.05 +speed_wall = =round(speed_print / 1.25, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2.25, 1) diff --git a/resources/variants/ultimaker2_plus_0.6.inst.cfg b/resources/variants/ultimaker2_plus_0.6.inst.cfg new file mode 100644 index 0000000000..34d0f7a5cf --- /dev/null +++ b/resources/variants/ultimaker2_plus_0.6.inst.cfg @@ -0,0 +1,16 @@ +[general] +name = 0.6 mm +version = 2 +definition = ultimaker2_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.6 +machine_nozzle_tip_outer_diameter = 1.25 +coasting_volume = 1.36 +speed_wall = =round(speed_print * 4 / 3, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2, 1) diff --git a/resources/variants/ultimaker2_plus_0.8.inst.cfg b/resources/variants/ultimaker2_plus_0.8.inst.cfg new file mode 100644 index 0000000000..e719409060 --- /dev/null +++ b/resources/variants/ultimaker2_plus_0.8.inst.cfg @@ -0,0 +1,16 @@ +[general] +name = 0.8 mm +version = 2 +definition = ultimaker2_plus + +[metadata] +author = Ultimaker +type = variant + +[values] +machine_nozzle_size = 0.8 +machine_nozzle_tip_outer_diameter = 1.35 +coasting_volume = 3.22 +speed_wall = =round(speed_print * 4 / 3, 1) +speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_topbottom = =round(speed_print / 2, 1)