diff --git a/.gitignore b/.gitignore index 72ba4bf565..dc951a52b7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ docs/html *.log resources/i18n/en resources/i18n/x-test +resources/firmware # Editors and IDEs. *kdev* @@ -15,3 +16,11 @@ resources/i18n/x-test *~ *.qm .idea + +# Eclipse+PyDev +.project +.pydevproject +.settings + +# Debian packaging +debian* diff --git a/CMakeLists.txt b/CMakeLists.txt index dc9f37c76e..98dca222b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ include(GNUInstallDirs) set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository") set(CURA_VERSION "master" CACHE STRING "Version name of Cura") +set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) # Macro needed to list all sub-directory of a directory. diff --git a/cura/ContainerSettingsModel.py b/cura/ContainerSettingsModel.py new file mode 100644 index 0000000000..2ff1a5f401 --- /dev/null +++ b/cura/ContainerSettingsModel.py @@ -0,0 +1,93 @@ +from UM.Application import Application +from UM.Qt.ListModel import ListModel + +from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl + +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.SettingFunction import SettingFunction + +class ContainerSettingsModel(ListModel): + LabelRole = Qt.UserRole + 1 + CategoryRole = Qt.UserRole + 2 + UnitRole = Qt.UserRole + 3 + ValuesRole = Qt.UserRole + 4 + + def __init__(self, parent = None): + super().__init__(parent) + self.addRoleName(self.LabelRole, "label") + self.addRoleName(self.CategoryRole, "category") + self.addRoleName(self.UnitRole, "unit") + self.addRoleName(self.ValuesRole, "values") + + self._container_ids = [] + self._containers = [] + + def _onPropertyChanged(self, key, property_name): + if property_name == "value": + self._update() + + def _update(self): + self.clear() + + if len(self._container_ids) == 0: + return + + keys = [] + for container in self._containers: + keys = keys + list(container.getAllKeys()) + + keys = list(set(keys)) # remove duplicate keys + keys.sort() + + for key in keys: + definition = None + category = None + values = [] + for container in self._containers: + instance = container.getInstance(key) + if instance: + definition = instance.definition + + # Traverse up to find the category + category = definition + while category.type != "category": + category = category.parent + + value = container.getProperty(key, "value") + if type(value) == SettingFunction: + values.append("=\u0192") + else: + values.append(container.getProperty(key, "value")) + else: + values.append("") + + self.appendItem({ + "key": key, + "values": values, + "label": definition.label, + "unit": definition.unit, + "category": category.label + }) + + ## Set the ids of the containers which have the settings this model should list. + # Also makes sure the model updates when the containers have property changes + def setContainers(self, container_ids): + for container in self._containers: + container.propertyChanged.disconnect(self._onPropertyChanged) + + self._container_ids = container_ids + self._containers = [] + + for container_id in self._container_ids: + containers = ContainerRegistry.getInstance().findContainers(id = container_id) + if containers: + containers[0].propertyChanged.connect(self._onPropertyChanged) + self._containers.append(containers[0]) + + self._update() + + containersChanged = pyqtSignal() + @pyqtProperty("QVariantList", fset = setContainers, notify = containersChanged) + def containers(self): + return self.container_ids \ No newline at end of file diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 8fb26858a5..39cdc36232 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -27,7 +27,9 @@ class ConvexHullDecorator(SceneNodeDecorator): # 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 + self._global_stack = None + Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) + self._onGlobalStackChanged() #Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged) #Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged) #self._onActiveProfileChanged() @@ -95,28 +97,30 @@ class ConvexHullDecorator(SceneNodeDecorator): def setConvexHullNode(self, node): self._convex_hull_node = node - def _onActiveProfileChanged(self): - if self._profile: - self._profile.settingValueChanged.disconnect(self._onSettingValueChanged) + def _onSettingValueChanged(self, key, property_name): + if key == "print_sequence" and property_name == "value": + self._onChanged() - self._profile = Application.getInstance().getMachineManager().getWorkingProfile() - - if self._profile: - self._profile.settingValueChanged.connect(self._onSettingValueChanged) - - def _onActiveMachineInstanceChanged(self): + def _onChanged(self, *args): if self._convex_hull_job: self._convex_hull_job.cancel() self.setConvexHull(None) - def _onSettingValueChanged(self, setting): - if setting == "print_sequence": - if self._convex_hull_job: - self._convex_hull_job.cancel() - self.setConvexHull(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() + + def _onGlobalStackChanged(self): + if self._global_stack: + self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged) + self._global_stack.containersChanged.disconnect(self._onChanged) + + self._global_stack = Application.getInstance().getGlobalContainerStack() + + if self._global_stack: + self._global_stack.propertyChanged.connect(self._onSettingValueChanged) + self._global_stack.containersChanged.connect(self._onChanged) + + self._onChanged() diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index de716fe25f..472a4e6424 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -18,6 +18,7 @@ from UM.JobQueue import JobQueue from UM.SaveFile import SaveFile from UM.Scene.Selection import Selection from UM.Scene.GroupDecorator import GroupDecorator +import UM.Settings.Validator from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation @@ -30,6 +31,8 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog +from . import ExtruderManager +from . import ExtrudersModel from . import PlatformPhysics from . import BuildVolume from . import CameraAnimation @@ -39,11 +42,13 @@ from . import MultiMaterialDecorator from . import ZOffsetDecorator from . import CuraSplashScreen from . import MachineManagerModel +from . import ContainerSettingsModel from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtGui import QColor, QIcon -from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType +from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType +import ast #For literal eval of extruder setting types. import platform import sys import os.path @@ -61,9 +66,10 @@ if platform.system() == "Linux": # Needed for platform.linux_distribution, which ctypes.CDLL(find_library('GL'), ctypes.RTLD_GLOBAL) try: - from cura.CuraVersion import CuraVersion + from cura.CuraVersion import CuraVersion, CuraBuildType except ImportError: CuraVersion = "master" # [CodeStyle: Reflecting imported value] + CuraBuildType = "" class CuraApplication(QtApplication): @@ -87,9 +93,13 @@ class CuraApplication(QtApplication): 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) + SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator) - super().__init__(name = "cura", version = CuraVersion) + super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) @@ -116,6 +126,7 @@ class CuraApplication(QtApplication): self._center_after_select = False self._camera_animation = None self._cura_actions = None + self._started = False self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity) self.getController().toolOperationStopped.connect(self._onToolOperationStopped) @@ -127,17 +138,34 @@ class CuraApplication(QtApplication): 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.ExtruderStack, "extruders") Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer) ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer) ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack) ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack) ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack) + # Add empty variant, material and quality containers. + # Since they are empty, they should never be serialized and instead just programmatically created. + # We need them to simplify the switching between materials. + empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() + empty_variant_container = copy.deepcopy(empty_container) + empty_variant_container._id = "empty_variant" + empty_variant_container.addMetaDataEntry("type", "variant") + ContainerRegistry.getInstance().addContainer(empty_variant_container) + empty_material_container = copy.deepcopy(empty_container) + empty_material_container._id = "empty_material" + empty_material_container.addMetaDataEntry("type", "material") + ContainerRegistry.getInstance().addContainer(empty_material_container) + empty_quality_container = copy.deepcopy(empty_container) + empty_quality_container._id = "empty_quality" + empty_quality_container.addMetaDataEntry("type", "quality") + ContainerRegistry.getInstance().addContainer(empty_quality_container) + ContainerRegistry.getInstance().load() Preferences.getInstance().addPreference("cura/active_mode", "simple") @@ -192,7 +220,7 @@ class CuraApplication(QtApplication): JobQueue.getInstance().jobFinished.connect(self._onJobFinished) - self.applicationShuttingDown.connect(self._onExit) + self.applicationShuttingDown.connect(self.saveSettings) self._recent_files = [] files = Preferences.getInstance().getValue("cura/recent_files").split(";") @@ -202,8 +230,13 @@ 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. - def _onExit(self): + ## 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 @@ -245,9 +278,15 @@ class CuraApplication(QtApplication): 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) + stack_type = stack.getMetaDataEntry("type", None) + path = None + if not stack_type or stack_type == "machine": + path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name) + elif stack_type == "extruder": + path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name) + if path: + with SaveFile(path, "wt", -1, "utf-8") as f: + f.write(data) @pyqtSlot(result = QUrl) @@ -258,6 +297,7 @@ class CuraApplication(QtApplication): # \sa PluginRegistery def _loadPlugins(self): self._plugin_registry.addType("profile_reader", self._addProfileReader) + self._plugin_registry.addType("profile_writer", self._addProfileWriter) self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura")) if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) @@ -320,6 +360,7 @@ class CuraApplication(QtApplication): self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface...")) + ExtruderManager.ExtruderManager.getInstance() #Initialise extruder so as to listen to global container stack changes before the first global container stack is set. qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager", MachineManagerModel.createMachineManagerModel) @@ -335,6 +376,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 @@ -351,6 +394,9 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information + ## Registers objects for the QML engine to use. + # + # \param engine The QML engine. def registerObjects(self, engine): engine.rootContext().setContextProperty("Printer", self) self._print_information = PrintInformation.PrintInformation() @@ -360,6 +406,21 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") + qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") + + qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") + + qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions") + + engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance()) + + for path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.QmlFiles): + type_name = os.path.splitext(os.path.basename(path))[0] + if type_name in ("Cura", "Actions"): + continue + + qmlRegisterType(QUrl.fromLocalFile(path), "Cura", 1, 0, type_name) + def onSelectionChanged(self): if Selection.hasSelection(): if not self.getController().getActiveTool(): @@ -419,21 +480,6 @@ class CuraApplication(QtApplication): self._platform_activity = True if count > 0 else False self.activityChanged.emit() - @pyqtSlot(str) - def setJobName(self, name): - # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its - # extension. This cuts the extension off if necessary. - name = os.path.splitext(name)[0] - if self._job_name != name: - self._job_name = name - self.jobNameChanged.emit() - - jobNameChanged = pyqtSignal() - - @pyqtProperty(str, notify = jobNameChanged) - def jobName(self): - return self._job_name - # Remove all selected objects from the scene. @pyqtSlot() def deleteSelection(self): @@ -702,16 +748,18 @@ class CuraApplication(QtApplication): def _onActiveMachineChanged(self): pass + fileLoaded = pyqtSignal(str) + def _onFileLoaded(self, job): node = job.getResult() if node != None: - self.setJobName(os.path.basename(job.getFileName())) + self.fileLoaded.emit(job.getFileName()) node.setSelectable(True) node.setName(os.path.basename(job.getFileName())) op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) op.push() - self.getController().getScene().sceneChanged.emit(node) #F orce scene change. + self.getController().getScene().sceneChanged.emit(node) #Force scene change. def _onJobFinished(self, job): if type(job) is not ReadMeshJob or not job.getResult(): @@ -744,3 +792,6 @@ class CuraApplication(QtApplication): def _addProfileReader(self, profile_reader): # TODO: Add the profile reader to the list of plug-ins that can be used when importing profiles. pass + + def _addProfileWriter(self, profile_writer): + pass diff --git a/cura/CuraSplashScreen.py b/cura/CuraSplashScreen.py index d27c9c0240..f2810d359b 100644 --- a/cura/CuraSplashScreen.py +++ b/cura/CuraSplashScreen.py @@ -21,6 +21,9 @@ class CuraSplashScreen(QSplashScreen): painter.setPen(QColor(0, 0, 0, 255)) version = Application.getInstance().getVersion().split("-") + buildtype = Application.getInstance().getBuildType() + if buildtype: + version[0] += " (%s)" %(buildtype) painter.setFont(QFont("Proxima Nova Rg", 20 )) painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0]) diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index bb69319ee6..5ad819b1fc 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -2,3 +2,4 @@ # Cura is released under the terms of the AGPLv3 or higher. CuraVersion = "@CURA_VERSION@" +CuraBuildType = "@CURA_BUILDTYPE@" \ No newline at end of file diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py new file mode 100644 index 0000000000..edd6ab7d23 --- /dev/null +++ b/cura/ExtruderManager.py @@ -0,0 +1,207 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant #For communicating data and events to Qt. + +import UM.Application #To get the global container stack to find the current machine. +import UM.Logger +import UM.Settings.ContainerRegistry #Finding containers by ID. + + +## Manages all existing extruder stacks. +# +# This keeps a list of extruder stacks for each machine. +class ExtruderManager(QObject): + ## Signal to notify other components when the list of extruders changes. + extrudersChanged = pyqtSignal(QVariant) + + ## Notify when the user switches the currently active extruder. + activeExtruderChanged = pyqtSignal() + + ## Registers listeners and such to listen to changes to the extruders. + def __init__(self, parent = None): + super().__init__(parent) + self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. + self._active_extruder_index = 0 + UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders) + + ## Gets the unique identifier of the currently active extruder stack. + # + # The currently active extruder stack is the stack that is currently being + # edited. + # + # \return The unique ID of the currently active extruder stack. + @pyqtProperty(str, notify = activeExtruderChanged) + def activeExtruderStackId(self): + if not UM.Application.getInstance().getGlobalContainerStack(): + return None #No active machine, so no active extruder. + try: + return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)].getId() + except KeyError: #Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. + return None + + ## The instance of the singleton pattern. + # + # It's None if the extruder manager hasn't been created yet. + __instance = None + + ## Gets an instance of the extruder manager, or creates one if no instance + # exists yet. + # + # This is an implementation of singleton. If an extruder manager already + # exists, it is re-used. + # + # \return The extruder manager. + @classmethod + def getInstance(cls): + if not cls.__instance: + cls.__instance = ExtruderManager() + return cls.__instance + + ## Changes the active extruder by index. + # + # \param index The index of the new active extruder. + @pyqtSlot(int) + def setActiveExtruderIndex(self, index): + self._active_extruder_index = index + self.activeExtruderChanged.emit() + + ## Adds all extruders of a specific machine definition to the extruder + # manager. + # + # \param machine_definition The machine to add the extruders for. + def addMachineExtruders(self, machine_definition): + machine_id = machine_definition.getId() + if machine_id not in self._extruder_trains: + self._extruder_trains[machine_id] = { } + + container_registry = UM.Settings.ContainerRegistry.getInstance() + if not container_registry: #Then we shouldn't have any machine definition either. In any case, there are no extruder trains then so bye bye. + return + + #Add the extruder trains that don't exist yet. + for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()): + position = extruder_definition.getMetaDataEntry("position", None) + if not position: + UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId()) + if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet. + self.createExtruderTrain(extruder_definition, machine_definition, position) + + #Gets the extruder trains that we just created as well as any that still existed. + extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId()) + for extruder_train in extruder_trains: + self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train + if extruder_trains: + self.extrudersChanged.emit(machine_definition) + + ## Creates a container stack for an extruder train. + # + # The container stack has an extruder definition at the bottom, which is + # linked to a machine definition. Then it has a nozzle profile, a material + # profile, a quality profile and a user profile, in that order. + # + # The resulting container stack is added to the registry. + # + # \param extruder_definition The extruder to create the extruder train + # for. + # \param machine_definition The machine that the extruder train belongs + # to. + # \param position The position of this extruder train in the extruder + # slots of the machine. + def createExtruderTrain(self, extruder_definition, machine_definition, position): + #Cache some things. + container_registry = UM.Settings.ContainerRegistry.getInstance() + machine_id = machine_definition.getId() + + #Create a container stack for this extruder. + extruder_stack_id = container_registry.uniqueName(extruder_definition.getId()) + container_stack = UM.Settings.ContainerStack(extruder_stack_id) + container_stack.setName(extruder_definition.getName()) #Take over the display name to display the stack with. + container_stack.addMetaDataEntry("type", "extruder_train") + container_stack.addMetaDataEntry("machine", machine_definition.getId()) + container_stack.addMetaDataEntry("position", position) + container_stack.addContainer(extruder_definition) + + #Find the nozzle to use for this extruder. + nozzle = container_registry.getEmptyInstanceContainer() + if machine_definition.getMetaDataEntry("has_nozzles", default = "False") == "True": + #First add any nozzle. Later, overwrite with preference if the preference is valid. + nozzles = container_registry.findInstanceContainers(machine = machine_id, type = "nozzle") + if len(nozzles) >= 1: + nozzle = nozzles[0] + preferred_nozzle_id = machine_definition.getMetaDataEntry("preferred_nozzle") + if preferred_nozzle_id: + preferred_nozzles = container_registry.findInstanceContainers(id = preferred_nozzle_id, type = "nozzle") + if len(preferred_nozzles) >= 1: + nozzle = preferred_nozzles[0] + else: + UM.Logger.log("w", "The preferred nozzle \"%s\" of machine %s doesn't exist or is not a nozzle profile.", preferred_nozzle_id, machine_id) + #And leave it at the default nozzle. + container_stack.addContainer(nozzle) + + #Find a material to use for this nozzle. + material = container_registry.getEmptyInstanceContainer() + if machine_definition.getMetaDataEntry("has_materials", default = "False") == "True": + #First add any material. Later, overwrite with preference if the preference is valid. + if machine_definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True": + materials = container_registry.findInstanceContainers(type = "material", machine = machine_id, nozzle = nozzle.getId()) + else: + materials = container_registry.findInstanceContainers(type = "material", machine = machine_id) + if len(materials) >= 1: + material = materials[0] + preferred_material_id = machine_definition.getMetaDataEntry("preferred_material") + if preferred_material_id: + preferred_materials = container_registry.findInstanceContainers(id = preferred_material_id, type = "material") + if len(preferred_materials) >= 1: + material = preferred_materials[0] + else: + UM.Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id) + #And leave it at the default material. + container_stack.addContainer(material) + + #Find a quality to use for this extruder. + quality = container_registry.getEmptyInstanceContainer() + if machine_definition.getMetaDataEntry("has_machine_quality"): + #First add any quality. Later, overwrite with preference if the preference is valid. + qualities = container_registry.findInstanceContainers(type = "quality") + if len(qualities) >= 1: + quality = qualities[0] + preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality") + if preferred_quality_id: + preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id.lower(), type = "quality") + if len(preferred_quality) >= 1: + quality = preferred_quality[0] + else: + UM.Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality_id, machine_id) + #And leave it at the default quality. + container_stack.addContainer(quality) + + user_profile = container_registry.findInstanceContainers(id = extruder_stack_id + "_current_settings") + if user_profile: #There was already a user profile, loaded from settings. + user_profile = user_profile[0] + else: + user_profile = UM.Settings.InstanceContainer(extruder_stack_id + "_current_settings") #Add an empty user profile. + user_profile.addMetaDataEntry("type", "user") + user_profile.setDefinition(machine_definition) + container_registry.addContainer(user_profile) + container_stack.addContainer(user_profile) + + container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + + container_registry.addContainer(container_stack) + + ## Generates extruders for a specific machine. + # + # \param machine_id The machine to get the extruders of. + def getMachineExtruders(self, machine_id): + if machine_id not in self._extruder_trains: + UM.Logger.log("w", "Tried to get the extruder trains for machine %s, which doesn't exist.", machine_id) + return + for name in self._extruder_trains[machine_id]: + yield self._extruder_trains[machine_id][name] + + ## Adds the extruders of the currently active machine. + def _addCurrentMachineExtruders(self): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + if global_stack and global_stack.getBottom(): + self.addMachineExtruders(global_stack.getBottom()) diff --git a/cura/ExtrudersModel.py b/cura/ExtrudersModel.py new file mode 100644 index 0000000000..960c05bd5e --- /dev/null +++ b/cura/ExtrudersModel.py @@ -0,0 +1,103 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty + +import cura.ExtruderManager +import UM.Qt.ListModel + +## Model that holds extruders. +# +# This model is designed for use by any list of extruders, but specifically +# intended for drop-down lists of the current machine's extruders in place of +# settings. +class ExtrudersModel(UM.Qt.ListModel.ListModel): + # The ID of the container stack for the extruder. + IdRole = Qt.UserRole + 1 + + ## Human-readable name of the extruder. + NameRole = Qt.UserRole + 2 + + ## Colour of the material loaded in the extruder. + ColourRole = Qt.UserRole + 3 + + ## Index of the extruder, which is also the value of the setting itself. + # + # An index of 0 indicates the first extruder, an index of 1 the second + # one, and so on. This is the value that will be saved in instance + # containers. + IndexRole = Qt.UserRole + 4 + + ## Colour to display if there is no material or the material has no known + # colour. + defaultColour = "#FFFF00" + + ## Initialises the extruders model, defining the roles and listening for + # changes in the data. + # + # \param parent Parent QtObject of this list. + def __init__(self, parent = None): + super().__init__(parent) + + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.ColourRole, "colour") + self.addRoleName(self.IndexRole, "index") + + self._add_global = False + + #Listen to changes. + manager = cura.ExtruderManager.ExtruderManager.getInstance() + manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general. + UM.Application.globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes. + self._updateExtruders() + + def setAddGlobal(self, add): + if add != self._add_global: + self._add_global = add + self._updateExtruders() + self.addGlobalChanged.emit() + + addGlobalChanged = pyqtSignal() + @pyqtProperty(bool, fset = setAddGlobal, notify = addGlobalChanged) + def addGlobal(self): + return self._add_global + + ## Update the list of extruders. + # + # This should be called whenever the list of extruders changes. + def _updateExtruders(self): + self.clear() + manager = cura.ExtruderManager.ExtruderManager.getInstance() + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + if not global_container_stack: + return #There is no machine to get the extruders of. + + if self._add_global: + material = global_container_stack.findContainer({ "type": "material" }) + colour = material.getMetaDataEntry("color_code", default = self.defaultColour) if material else self.defaultColour + item = { + "id": global_container_stack.getId(), + "name": "Global", + "colour": colour, + "index": -1 + } + self.appendItem(item) + + for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()): + material = extruder.findContainer({ "type": "material" }) + colour = material.getMetaDataEntry("color_code", default = self.defaultColour) if material else self.defaultColour + position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition. + try: + position = int(position) + except ValueError: #Not a proper int. + position = -1 + item = { #Construct an item with only the relevant information. + "id": extruder.getId(), + "name": extruder.getName(), + "colour": colour, + "index": position + } + self.appendItem(item) + + self.sort(lambda item: item["index"]) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 7b42483fca..443c19d302 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -1,11 +1,18 @@ +import re + from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from UM.Application import Application from UM.Preferences import Preferences import UM.Settings +from UM.Settings.Validator import ValidatorState +from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.ContainerStack import ContainerStack +from . import ExtruderManager +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") -import re class MachineManagerModel(QObject): def __init__(self, parent = None): @@ -13,6 +20,7 @@ class MachineManagerModel(QObject): self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) + self._global_stack_valid = None self._onGlobalContainerChanged() ## When the global container is changed, active material probably needs to be updated. @@ -20,6 +28,10 @@ class MachineManagerModel(QObject): self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) + self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0] + self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0] + self._empty_quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0] + Preferences.getInstance().addPreference("cura/active_machine", "") active_machine_id = Preferences.getInstance().getValue("cura/active_machine") @@ -29,15 +41,42 @@ class MachineManagerModel(QObject): self.setActiveMachine(active_machine_id) pass - globalContainerChanged = pyqtSignal() activeMaterialChanged = pyqtSignal() activeVariantChanged = pyqtSignal() activeQualityChanged = pyqtSignal() + globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. + globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed. + + @pyqtProperty("QVariantMap", notify = globalContainerChanged) + def extrudersIds(self): + ## Find all extruders that reference the new stack + extruders = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(**{"machine": self._global_container_stack.getId()}) + result = {} + for extruder in extruders: + result[extruder.getMetaDataEntry("position")] = extruder.getId() + return result + + def _onGlobalPropertyChanged(self, key, property_name): + if property_name == "value": + self.globalValueChanged.emit() + if property_name == "validationState": + if self._global_stack_valid: + changed_validation_state = self._global_container_stack.getProperty(key, property_name) + if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + self._global_stack_valid = False + self.globalValidationChanged.emit() + else: + new_validation_state = self._checkStackForErrors(self._global_container_stack) + if new_validation_state: + self._global_stack_valid = True + self.globalValidationChanged.emit() + def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged) + self._global_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() self.globalContainerChanged.emit() @@ -45,6 +84,8 @@ class MachineManagerModel(QObject): if self._global_container_stack: Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId()) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) + self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) + self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") @@ -62,55 +103,18 @@ class MachineManagerModel(QObject): Application.getInstance().setGlobalContainerStack(containers[0]) @pyqtSlot(str, str) - def addMachine(self,name, definition_id): - definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id=definition_id) + 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()) - + name = self._createUniqueName("machine", "", name, definition.getName()) new_global_stack = UM.Settings.ContainerStack(name) new_global_stack.addMetaDataEntry("type", "machine") UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack) - 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] + variant_instance_container = self._updateVariantContainer(definition) + material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) + quality_instance_container = self._updateQualityContainer(definition, material_instance_container) current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings") current_settings_instance_container.addMetaDataEntry("machine", name) @@ -119,7 +123,7 @@ class MachineManagerModel(QObject): 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]) + new_global_stack.addContainer(definition) if variant_instance_container: new_global_stack.addContainer(variant_instance_container) if material_instance_container: @@ -128,27 +132,85 @@ class MachineManagerModel(QObject): new_global_stack.addContainer(quality_instance_container) new_global_stack.addContainer(current_settings_instance_container) + ExtruderManager.ExtruderManager.getInstance().addMachineExtruders(definition) + Application.getInstance().setGlobalContainerStack(new_global_stack) - # 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 + ## Create a name that is not empty and unique + # \param container_type \type{string} Type of the container (machine, quality, ...) + # \param current_name \type{} Current name of the container, which may be an acceptable option + # \param new_name \type{string} Base name, which may not be unique + # \param fallback_name \type{string} Name to use when (stripped) new_name is empty + # \return \type{string} Name that is unique for the specified type and name/id + def _createUniqueName(self, container_type, current_name, new_name, fallback_name): + new_name = new_name.strip() + num_check = re.compile("(.*?)\s*#\d+$").match(new_name) + if num_check: + new_name = num_check.group(1) + if new_name == "": + new_name = fallback_name - #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) + unique_name = new_name + i = 1 + # In case we are renaming, the current name of the container is also a valid end-result + while self._containerExists(container_type, unique_name) and unique_name != current_name: + i += 1 + unique_name = "%s #%d" % (new_name, i) return unique_name + ## Check if a container with of a certain type and a certain name or id exists + # Both the id and the name are checked, because they may not be the same and it is better if they are both unique + # \param container_type \type{string} Type of the container (machine, quality, ...) + # \param container_name \type{string} Name to check + def _containerExists(self, container_type, container_name): + container_class = ContainerStack if container_type == "machine" else InstanceContainer + + return UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, id = container_name, type = container_type) or \ + UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, name = container_name, type = container_type) + + ## Convenience function to check if a stack has errors. + def _checkStackForErrors(self, stack): + if stack is None: + return False + + for key in stack.getAllKeys(): + validation_state = stack.getProperty(key, "validationState") + if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + return True + return False + + ## Remove all instances from the top instanceContainer (effectively removing all user-changed settings) + @pyqtSlot() + def clearUserSettings(self): + if not self._global_container_stack: + return + user_settings = self._global_container_stack.getTop() + user_settings.clear() + + ## Check if the global_container has instances in the user container + @pyqtProperty(bool, notify = globalValueChanged) + def hasUserSettings(self): + if not self._global_container_stack: + return + + user_settings = self._global_container_stack.getTop().findInstances(**{}) + return len(user_settings) != 0 + + ## Check if the global profile does not contain error states + # Note that the _global_stack_valid is cached due to performance issues + # Calling _checkStackForErrors on every change is simply too expensive + @pyqtProperty(bool, notify = globalValidationChanged) + def isGlobalStackValid(self): + return self._global_stack_valid + + @pyqtProperty(str, notify = globalContainerChanged) + def activeUserProfileId(self): + if self._global_container_stack: + return self._global_container_stack.getTop().getId() + + return "" + @pyqtProperty(str, notify = globalContainerChanged) def activeMachineName(self): if self._global_container_stack: @@ -197,6 +259,101 @@ class MachineManagerModel(QObject): return quality.getId() return "" + ## Check if a container is read_only + @pyqtSlot(str, result = bool) + def isReadOnly(self, container_id): + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=container_id) + if not containers or not self._global_container_stack: + return True + return containers[0].isReadOnly() + + @pyqtSlot(result = str) + def newQualityContainerFromQualityAndUser(self): + new_container_id = self.duplicateContainer(self.activeQualityId) + if new_container_id == "": + return + self.setActiveQuality(new_container_id) + self.updateQualityContainerFromUserContainer() + + + @pyqtSlot(str, result=str) + def duplicateContainer(self, container_id): + if not self._global_container_stack: + return "" + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=container_id) + if containers: + new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile")) + + new_container = InstanceContainer("") + + ## Copy all values + new_container.deserialize(containers[0].serialize()) + + new_container.setReadOnly(False) + new_container.setName(new_name) + new_container._id = new_name + UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) + return new_name + + return "" + + @pyqtSlot(str, str) + def renameQualityContainer(self, container_id, new_name): + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality") + if containers: + new_name = self._createUniqueName("quality", containers[0].getName(), new_name, + catalog.i18nc("@label", "Custom profile")) + + # As we also want the id of the container to be changed (so that profile name is the name of the file + # on disk. We need to create a new instance and remove it (so the old file of the container is removed) + # If we don't do that, we might get duplicates & other weird issues. + new_container = InstanceContainer("") + new_container.deserialize(containers[0].serialize()) + + # Actually set the name + new_container.setName(new_name) + new_container._id = new_name # Todo: Fix proper id change function for this. + + # Add the "new" container. + UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) + + # Ensure that the renamed profile is saved -before- we remove the old profile. + Application.getInstance().saveSettings() + + # Actually set & remove new / old quality. + self.setActiveQuality(new_name) + self.removeQualityContainer(containers[0].getId()) + + @pyqtSlot(str) + def removeQualityContainer(self, container_id): + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) + if not containers or not self._global_container_stack: + return + + # If the container that is being removed is the currently active container, set another machine as the active container + activate_new_container = container_id == self.activeQualityId + + UM.Settings.ContainerRegistry.getInstance().removeContainer(container_id) + + if activate_new_container: + definition_id = "fdmprinter" if not self.filterQualityByMachine else self.activeDefinitionId + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "quality", definition = definition_id) + if containers: + self.setActiveQuality(containers[0].getId()) + self.activeQualityChanged.emit() + + + @pyqtSlot() + def updateQualityContainerFromUserContainer(self): + if not self._global_container_stack: + return + user_settings = self._global_container_stack.getTop() + quality = self._global_container_stack.findContainer({"type": "quality"}) + for key in user_settings.getAllKeys(): + quality.setProperty(key, "value", user_settings.getProperty(key, "value")) + self.clearUserSettings() # As all users settings are noq a quality, remove them. + + @pyqtSlot(str) def setActiveMaterial(self, material_id): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=material_id) @@ -208,6 +365,8 @@ class MachineManagerModel(QObject): material_index = self._global_container_stack.getContainerIndex(old_material) self._global_container_stack.replaceContainer(material_index, containers[0]) + self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0]).id) + @pyqtSlot(str) def setActiveVariant(self, variant_id): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=variant_id) @@ -219,6 +378,8 @@ class MachineManagerModel(QObject): variant_index = self._global_container_stack.getContainerIndex(old_variant) self._global_container_stack.replaceContainer(variant_index, containers[0]) + self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0]).id) + @pyqtSlot(str) def setActiveQuality(self, quality_id): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id) @@ -261,31 +422,116 @@ class MachineManagerModel(QObject): 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()) + new_name = self._createUniqueName("machine", containers[0].getName(), new_name, containers[0].getBottom().getName()) containers[0].setName(new_name) + self.globalContainerChanged.emit() @pyqtSlot(str) def removeMachine(self, machine_id): # If the machine that is being removed is the currently active machine, set another machine as the active machine - 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]) + activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) + + current_settings_id = machine_id + "_current_settings" + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = current_settings_id) + for container in containers: + UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId()) UM.Settings.ContainerRegistry.getInstance().removeContainer(machine_id) + if activate_new_machine: + stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(type = "machine") + if stacks: + Application.getInstance().setGlobalContainerStack(stacks[0]) + + @pyqtProperty(bool, notify = globalContainerChanged) def hasMaterials(self): if self._global_container_stack: - return self._global_container_stack.getMetaDataEntry("has_materials", False) + return bool(self._global_container_stack.getMetaDataEntry("has_materials", False)) return False @pyqtProperty(bool, notify = globalContainerChanged) def hasVariants(self): if self._global_container_stack: - return self._global_container_stack.getMetaDataEntry("has_variants", False) + return bool(self._global_container_stack.getMetaDataEntry("has_variants", False)) return False + @pyqtProperty(bool, notify = globalContainerChanged) + def filterMaterialsByMachine(self): + if self._global_container_stack: + return bool(self._global_container_stack.getMetaDataEntry("has_machine_materials", False)) + + return False + + @pyqtProperty(bool, notify = globalContainerChanged) + def filterQualityByMachine(self): + if self._global_container_stack: + return bool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False)) + + return False + + def _updateVariantContainer(self, definition): + if not definition.getMetaDataEntry("has_variants"): + return self._empty_variant_container + + containers = [] + preferred_variant = definition.getMetaDataEntry("preferred_variant") + if preferred_variant: + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = definition.id, id = preferred_variant) + + if not containers: + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = definition.id) + + if containers: + return containers[0] + + return self._empty_variant_container + + def _updateMaterialContainer(self, definition, variant_container = None): + if not definition.getMetaDataEntry("has_materials"): + return self._empty_material_container + + search_criteria = { "type": "material" } + + if definition.getMetaDataEntry("has_machine_materials"): + search_criteria["definition"] = definition.id + + if definition.getMetaDataEntry("has_variants") and variant_container: + search_criteria["variant"] = variant_container.id + else: + search_criteria["definition"] = "fdmprinter" + + preferred_material = definition.getMetaDataEntry("preferred_material") + if preferred_material: + search_criteria["id"] = preferred_material + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + return containers[0] + + return self._empty_material_container + + def _updateQualityContainer(self, definition, material_container = None): + search_criteria = { "type": "quality" } + + if definition.getMetaDataEntry("has_machine_quality"): + search_criteria["definition"] = definition.id + + if definition.getMetaDataEntry("has_materials") and material_container: + search_criteria["material"] = material_container.id + else: + search_criteria["definition"] = "fdmprinter" + + preferred_quality = definition.getMetaDataEntry("preferred_quality") + if preferred_quality: + search_criteria["id"] = preferred_quality + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + return containers[0] + + return self._empty_quality_container + def createMachineManagerModel(engine, script_engine): return MachineManagerModel() diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 2663cab5a0..f1eb93de0e 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -1,14 +1,17 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty +from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from UM.Application import Application from UM.Qt.Duration import Duration +from UM.Preferences import Preferences import math +import os.path +import unicodedata -## A class for processing and calculating minimum, current and maximum print time. +## A class for processing and calculating minimum, current and maximum print time as well as managing the job name # # This class contains all the logic relating to calculation and slicing for the # time/quality slider concept. It is a rather tricky combination of event handling @@ -22,6 +25,8 @@ import math # - When that is done, we update the minimum print time and start the final slice pass, the "high quality settings pass". # - When the high quality pass is done, we update the maximum print time. # +# This class also mangles the current machine name and the filename of the first loaded mesh into a job name. +# This job name is requested by the JobSpecs qml file. class PrintInformation(QObject): class SlicePass: CurrentSettings = 1 @@ -45,14 +50,20 @@ class PrintInformation(QObject): if self._backend: self._backend.printDurationMessage.connect(self._onPrintDurationMessage) + self._job_name = "" + self._abbr_machine = "" + + Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName) + Application.getInstance().fileLoaded.connect(self.setJobName) + currentPrintTimeChanged = pyqtSignal() - + @pyqtProperty(Duration, notify = currentPrintTimeChanged) def currentPrintTime(self): return self._current_print_time materialAmountChanged = pyqtSignal() - + @pyqtProperty(float, notify = materialAmountChanged) def materialAmount(self): return self._material_amount @@ -66,3 +77,46 @@ class PrintInformation(QObject): r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2) self.materialAmountChanged.emit() + + @pyqtSlot(str) + def setJobName(self, name): + # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its + # extension. This cuts the extension off if necessary. + name = os.path.splitext(name)[0] + if self._job_name != name: + self._job_name = name + self.jobNameChanged.emit() + + jobNameChanged = pyqtSignal() + + @pyqtProperty(str, notify = jobNameChanged) + def jobName(self): + return self._job_name + + @pyqtSlot(str, result = str) + def createJobName(self, base_name): + base_name = self._stripAccents(base_name) + if Preferences.getInstance().getValue("cura/jobname_prefix"): + return self._abbr_machine + "_" + base_name + else: + return base_name + + ## Created an acronymn-like abbreviated machine name from the currently active machine name + # Called each time the global stack is switched + def _setAbbreviatedMachineName(self): + global_stack_name = Application.getInstance().getGlobalContainerStack().getName() + split_name = global_stack_name.split(" ") + abbr_machine = "" + for word in split_name: + if word.lower() == "ultimaker": + abbr_machine += "UM" + elif word.isdigit(): + abbr_machine += word + else: + abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0] + + self._abbr_machine = abbr_machine + + ## Utility method that strips accents from characters (eg: â -> a) + def _stripAccents(self, str): + return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn') \ No newline at end of file diff --git a/cura/ProfileWriter.py b/cura/ProfileWriter.py new file mode 100644 index 0000000000..6c719205ec --- /dev/null +++ b/cura/ProfileWriter.py @@ -0,0 +1,25 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Uranium is released under the terms of the AGPLv3 or higher. + +from UM.PluginObject import PluginObject + +## Base class for profile writer plugins. +# +# This class defines a write() function to write profiles to files with. +class ProfileWriter(PluginObject): + ## Initialises the profile writer. + # + # This currently doesn't do anything since the writer is basically static. + def __init__(self): + super().__init__() + + ## Writes a profile to the specified file path. + # + # The profile writer may write its own file format to the specified file. + # + # \param path \type{string} The file to output to. + # \param profile \type{Profile} The profile to write to the file. + # \return \code True \endcode if the writing was successful, or \code + # False \endcode if it wasn't. + def write(self, path, node): + raise NotImplementedError("Profile writer plugin was not correctly implemented. No write was specified.") diff --git a/cura/SettingOverrideDecorator.py b/cura/SettingOverrideDecorator.py index 3e8815ec67..7417e47bf2 100644 --- a/cura/SettingOverrideDecorator.py +++ b/cura/SettingOverrideDecorator.py @@ -1,31 +1,78 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from UM.Scene.SceneNodeDecorator import SceneNodeDecorator +import copy + +from UM.Scene.SceneNodeDecorator import SceneNodeDecorator +from UM.Signal import Signal, signalemitter from UM.Settings.ContainerStack import ContainerStack from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry +import UM.Logger from UM.Application import Application ## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding # the linked node. The Stack in question will refer to the global stack (so that settings that are not defined by # this stack still resolve. +@signalemitter class SettingOverrideDecorator(SceneNodeDecorator): + ## Event indicating that the user selected a different extruder. + activeExtruderChanged = Signal() + def __init__(self): super().__init__() self._stack = ContainerStack(stack_id = id(self)) + self._stack.setDirty(False) # This stack does not need to be saved. self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer") self._stack.addContainer(self._instance) + self._extruder_stack = None #Stack upon which our stack is based. + + self._stack.propertyChanged.connect(self._onSettingChanged) ContainerRegistry.getInstance().addContainer(self._stack) - Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) - self._onGlobalContainerStackChanged() + Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack) + self.activeExtruderChanged.connect(self._updateNextStack) + self._updateNextStack() - def _onGlobalContainerStackChanged(self): - ## Ensure that the next stack is always the global stack. - self._stack.setNextStack(Application.getInstance().getGlobalContainerStack()) + def __deepcopy__(self, memo): + ## Create a fresh decorator object + deep_copy = SettingOverrideDecorator() + ## Copy the instance + deep_copy._instance = copy.deepcopy(self._instance, memo) + ## Set the copied instance as the first (and only) instance container of the stack. + deep_copy._stack.replaceContainer(0, deep_copy._instance) + return deep_copy + + ## Gets the currently active extruder to print this object with. + # + # \return An extruder's container stack. + def getActiveExtruder(self): + return self._extruder_stack + + def _onSettingChanged(self, instance, property): + if property == "value": # Only reslice if the value has changed. + Application.getInstance().getBackend().forceSlice() + + ## Makes sure that the stack upon which the container stack is placed is + # kept up to date. + def _updateNextStack(self): + if self._extruder_stack: + extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack) + if extruder_stack: + self._stack.setNextStack(extruder_stack) + else: + UM.Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack) + else: + self._stack.setNextStack(Application.getInstance().getGlobalContainerStack()) + + ## Changes the extruder with which to print this node. + # + # \param extruder_stack_id The new extruder stack to print with. + def setActiveExtruder(self, extruder_stack_id): + self._extruder_stack = extruder_stack_id + self.activeExtruderChanged.emit() def getStack(self): return self._stack \ No newline at end of file diff --git a/cura_app.py b/cura_app.py index dc748435f9..3bcce18fb5 100755 --- a/cura_app.py +++ b/cura_app.py @@ -36,7 +36,6 @@ import Arcus #@UnusedImport import cura.CuraApplication if sys.platform == "win32" and hasattr(sys, "frozen"): - import os dirpath = os.path.expanduser("~/AppData/Local/cura/") os.makedirs(dirpath, exist_ok = True) sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w") 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.py b/plugins/ChangeLogPlugin/ChangeLog.py index 7c8c81f2c6..d004104f91 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.py +++ b/plugins/ChangeLogPlugin/ChangeLog.py @@ -48,7 +48,8 @@ class ChangeLog(Extension, QObject,): result += "

" + str(version) + "


" result += "" for change in logs[version]: - result += "" + str(change) + "
" + if str(change) != "": + result += "" + str(change) + "
" for line in logs[version][change]: result += str(line) + "
" result += "
" @@ -60,20 +61,21 @@ class ChangeLog(Extension, QObject,): self._change_logs = collections.OrderedDict() with open(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"), "r",-1, "utf-8") as f: open_version = None - open_header = None + open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog for line in f: line = line.replace("\n","") if "[" in line and "]" in line: line = line.replace("[","") line = line.replace("]","") open_version = Version(line) - self._change_logs[Version(line)] = collections.OrderedDict() + self._change_logs[open_version] = collections.OrderedDict() elif line.startswith("*"): open_header = line.replace("*","") self._change_logs[open_version][open_header] = [] - else: - if line != "": - self._change_logs[open_version][open_header].append(line) + elif line != "": + if open_header not in self._change_logs[open_version]: + self._change_logs[open_version][open_header] = [] + self._change_logs[open_version][open_header].append(line) def _onEngineCreated(self): if not self._version: @@ -105,4 +107,3 @@ class ChangeLog(Extension, QObject,): self._changelog_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._changelog_context.setContextProperty("manager", self) self._changelog_window = component.create(self._changelog_context) - #print(self._changelog_window) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index d60154d56b..48e96ce4b6 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -1,7 +1,6 @@ -[2.1.0] +[2.1.2] -*2.1 Beta release -Cura has been completely reengineered from the ground up for an even more seamless integration between hardware, software and materials. Together with its intuitive new user interface, it’s now also ready for any future developments. For the beginner, Cura makes 3D printing incredibly easy, and for more advanced users, there are over 140 new customisable settings. +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/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 0d4975aca4..38753fd804 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -5,12 +5,20 @@ package cura.proto; message ObjectList { repeated Object objects = 1; - repeated Setting settings = 2; + repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing) } message Slice { - repeated ObjectList object_lists = 1; + repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another + SettingList global_settings = 2; // The global settings used for the whole print job + repeated Extruder extruders = 3; // The settings sent to each extruder object +} + +message Extruder +{ + int32 id = 1; + SettingList settings = 2; } message Object @@ -29,10 +37,10 @@ message Progress message Layer { int32 id = 1; - float height = 2; - float thickness = 3; + float height = 2; // Z position + float thickness = 3; // height of a single layer - repeated Polygon polygons = 4; + repeated Polygon polygons = 4; // layer data } message Polygon { @@ -48,19 +56,19 @@ message Polygon { MoveCombingType = 8; MoveRetractionType = 9; } - Type type = 1; - bytes points = 2; - float line_width = 3; + Type type = 1; // Type of move + bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) + float line_width = 3; // The width of the line being laid down } message GCodeLayer { bytes data = 2; } -message ObjectPrintTime { +message ObjectPrintTime { // The print time for the whole print and material estimates for the first extruder int64 id = 1; - float time = 2; - float material_amount = 3; + float time = 2; // Total time estimate + float material_amount = 3; // material used in the first extruder } message SettingList { @@ -68,13 +76,13 @@ message SettingList { } message Setting { - string name = 1; + string name = 1; // Internal key to signify a setting - bytes value = 2; + bytes value = 2; // The value of the setting } message GCodePrefix { - bytes data = 2; + bytes data = 2; // Header string to be prenpended before the rest of the gcode sent from the engine } message SlicingFinished { diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 9607ba407b..3e22f8f6fb 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -145,8 +145,7 @@ class CuraEngineBackend(Backend): self.slicingStarted.emit() slice_message = self._socket.createMessage("cura.proto.Slice") - settings_message = self._socket.createMessage("cura.proto.SettingList") - self._start_slice_job = StartSliceJob.StartSliceJob(slice_message, settings_message) + self._start_slice_job = StartSliceJob.StartSliceJob(slice_message) self._start_slice_job.start() self._start_slice_job.finished.connect(self._onStartSliceCompleted) @@ -205,7 +204,6 @@ class CuraEngineBackend(Backend): 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. diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 59feec0fc2..46b2d24bc5 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -103,7 +103,7 @@ class ProcessSlicedLayersJob(Job): Job.yieldThread() Job.yieldThread() current_layer += 1 - progress = (current_layer / layer_count) * 100 + progress = (current_layer / layer_count) * 99 # TODO: Rebuild the layer data mesh once the layer has been processed. # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh. diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index cf008e7b6f..3d2eb0ed4a 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -3,7 +3,6 @@ import numpy from string import Formatter -import traceback from enum import IntEnum from UM.Job import Job @@ -16,6 +15,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.Validator import ValidatorState from cura.OneAtATimeIterator import OneAtATimeIterator +from cura.ExtruderManager import ExtruderManager class StartJobResult(IntEnum): Finished = 1 @@ -38,20 +38,30 @@ class GcodeStartEndFormatter(Formatter): ## Job class that builds up the message of scene data to send to CuraEngine. class StartSliceJob(Job): - def __init__(self, slice_message, settings_message): + def __init__(self, slice_message): super().__init__() self._scene = Application.getInstance().getController().getScene() self._slice_message = slice_message - self._settings_message = settings_message self._is_cancelled = False - def getSettingsMessage(self): - return self._settings_message - def getSliceMessage(self): return self._slice_message + ## Check if a stack has any errors. + ## returns true if it has errors, false otherwise. + def _checkStackForErrors(self, stack): + if stack is None: + return False + + for key in stack.getAllKeys(): + validation_state = stack.getProperty(key, "validationState") + if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state) + return True + Job.yieldThread() + return False + ## Runs the job that initiates the slicing. def run(self): stack = Application.getInstance().getGlobalContainerStack() @@ -59,16 +69,20 @@ class StartSliceJob(Job): self.setResult(StartJobResult.Error) return - #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) + # Don't slice if there is a setting with an error value. + if self._checkStackForErrors(stack): + self.setResult(StartJobResult.SettingError) + return + + # Don't slice if there is a per object setting with an error value. + for node in DepthFirstIterator(self._scene.getRoot()): + if type(node) is not SceneNode or not node.isSelectable(): + continue + + if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.SettingError) return - Job.yieldThread() - with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator(self._scene.getRoot()): @@ -114,6 +128,9 @@ class StartSliceJob(Job): self._buildGlobalSettingsMessage(stack) + for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()): + self._buildExtruderMessage(extruder_stack) + for group in object_groups: group_message = self._slice_message.addRepeatedMessage("object_lists") if group[0].getParent().callDecoration("isGroup"): @@ -153,6 +170,15 @@ class StartSliceJob(Job): Logger.logException("w", "Unable to do token replacement on start/end gcode") return str(value).encode("utf-8") + def _buildExtruderMessage(self, stack): + message = self._slice_message.addRepeatedMessage("extruders") + message.id = int(stack.getMetaDataEntry("position")) + for key in stack.getAllKeys(): + setting = message.getMessage("settings").addRepeatedMessage("settings") + setting.name = key + setting.value = str(stack.getProperty(key, "value")).encode("utf-8") + Job.yieldThread() + ## Sends all global settings to the engine. # # The settings are taken from the global stack. This does not include any @@ -168,7 +194,7 @@ class StartSliceJob(Job): 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 = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings") setting_message.name = key if key == "machine_start_gcode" or key == "machine_end_gcode": #If it's a g-code message, use special formatting. setting_message.value = self._expandGcodeTokens(key, value, settings) @@ -176,21 +202,10 @@ class StartSliceJob(Job): setting_message.value = str(value).encode("utf-8") def _handlePerObjectSettings(self, node, message): - profile = node.callDecoration("getProfile") - if profile: - for key, value in profile.getAllSettingValues().items(): + stack = node.callDecoration("getStack") + if stack: + for key in stack.getAllKeys(): setting = message.addRepeatedMessage("settings") setting.name = key - setting.value = str(value).encode() - - Job.yieldThread() - - object_settings = node.callDecoration("getAllSettingValues") - if not object_settings: - return - for key, value in object_settings.items(): - setting = message.addRepeatedMessage("settings") - setting.name = key - setting.value = str(value).encode() - - Job.yieldThread() + setting.value = str(stack.getProperty(key, "value")).encode("utf-8") + Job.yieldThread() \ No newline at end of file diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index 1d27649498..b9c1f208ea 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -1,11 +1,12 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +import os.path + from UM.Application import Application #To get the machine manager to create the new profile in. from UM.Logger import Logger -from UM.Settings.Profile import Profile -from UM.Settings.ProfileReader import ProfileReader - +from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make. +from cura.ProfileReader import ProfileReader ## A plugin that reads profile data from Cura profile files. # @@ -25,17 +26,17 @@ class CuraProfileReader(ProfileReader): # returned. def read(self, file_name): # Create an empty profile. - profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) - serialised = "" + profile = InstanceContainer(os.path.basename(os.path.splitext(file_name)[0])) + profile.addMetaDataEntry("type", "quality") try: with open(file_name) as f: # Open file for reading. - serialised = f.read() + serialized = f.read() except IOError as e: Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e)) return None try: - profile.unserialise(serialised) + profile.deserialize(serialized) except Exception as e: # Parsing error. This is not a (valid) Cura profile then. Logger.log("e", "Error while trying to parse profile: %s", str(e)) return None diff --git a/plugins/CuraProfileReader/__init__.py b/plugins/CuraProfileReader/__init__.py index bfaa16ed5e..c4206ab763 100644 --- a/plugins/CuraProfileReader/__init__.py +++ b/plugins/CuraProfileReader/__init__.py @@ -13,7 +13,7 @@ def getMetaData(): "author": "Ultimaker", "version": "1.0", "description": catalog.i18nc("@info:whatsthis", "Provides support for importing Cura profiles."), - "api": 2 + "api": 3 }, "profile_reader": [ { diff --git a/plugins/CuraProfileWriter/CuraProfileWriter.py b/plugins/CuraProfileWriter/CuraProfileWriter.py index 82df446b8a..86b4f7dc89 100644 --- a/plugins/CuraProfileWriter/CuraProfileWriter.py +++ b/plugins/CuraProfileWriter/CuraProfileWriter.py @@ -4,7 +4,7 @@ from UM.Logger import Logger from UM.SaveFile import SaveFile -from UM.Settings.ProfileWriter import ProfileWriter +from cura.ProfileWriter import ProfileWriter ## Writes profiles to Cura's own profile format with config files. @@ -16,10 +16,10 @@ class CuraProfileWriter(ProfileWriter): # \return \code True \endcode if the writing was successful, or \code # False \endcode if it wasn't. def write(self, path, profile): - serialised = profile.serialise() + serialized = profile.serialize() try: with SaveFile(path, "wt", -1, "utf-8") as f: # Open the specified file. - f.write(serialised) + f.write(serialized) except Exception as e: Logger.log("e", "Failed to write profile to %s: %s", path, str(e)) return False diff --git a/plugins/CuraProfileWriter/__init__.py b/plugins/CuraProfileWriter/__init__.py index 43890de469..30528b8167 100644 --- a/plugins/CuraProfileWriter/__init__.py +++ b/plugins/CuraProfileWriter/__init__.py @@ -13,7 +13,7 @@ def getMetaData(): "author": "Ultimaker", "version": "1.0", "description": catalog.i18nc("@info:whatsthis", "Provides support for exporting Cura profiles."), - "api": 2 + "api": 3 }, "profile_writer": [ { 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 a98fab5c8a..4a73f5de9c 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -10,6 +10,7 @@ from UM.Scene.Selection import Selection from UM.Math.Color import Color from UM.Mesh.MeshData import MeshData from UM.Job import Job +from UM.Preferences import Preferences from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL @@ -41,7 +42,10 @@ class LayerView(View): self._top_layers_job = None self._activity = False - self._solid_layers = 5 + Preferences.getInstance().addPreference("view/top_layer_count", 1) + Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + + self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._top_layer_timer = QTimer() self._top_layer_timer.setInterval(50) @@ -209,6 +213,16 @@ class LayerView(View): self._top_layers_job = None + def _onPreferencesChanged(self, preference): + if preference != "view/top_layer_count": + return + + self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) + + self._current_layer_mesh = None + self._current_layer_jumps = None + self._top_layer_timer.start() + class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): super().__init__() diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py index 9ef2515bed..381d45b1c2 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -5,7 +5,8 @@ from UM.Logger import Logger from cura.SettingOverrideDecorator import SettingOverrideDecorator - +## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container +# are returned as visible. class PerObjectSettingVisibilityHandler(QObject): def __init__(self, parent = None, *args, **kwargs): super().__init__(parent = parent, *args, **kwargs) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py b/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py deleted file mode 100644 index 74a1fbed9a..0000000000 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsModel.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2015 Ultimaker B.V. -# Uranium is released under the terms of the AGPLv3 or higher. - -from PyQt5.QtCore import Qt, pyqtSlot, QUrl - -from UM.Application import Application -from UM.Qt.ListModel import ListModel -from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator -from UM.Scene.SceneNode import SceneNode -#from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator -#from UM.Settings.ProfileOverrideDecorator import ProfileOverrideDecorator - -from . import SettingOverrideModel - -class PerObjectSettingsModel(ListModel): - IdRole = Qt.UserRole + 1 # 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._updateModel() - - @pyqtSlot("quint64", str) - def setObjectProfile(self, object_id, profile_name): - self.setProperty(self.find("id", object_id), "profile", profile_name) - - profile = None - '''if profile_name != "global": - profile = Application.getInstance().getMachineManager().findProfile(profile_name) - - node = self._scene.findObject(object_id) - if profile: - if not node.getDecorator(ProfileOverrideDecorator): - node.addDecorator(ProfileOverrideDecorator()) - node.callDecoration("setProfile", profile) - else: - if node.getDecorator(ProfileOverrideDecorator): - node.removeDecorator(ProfileOverrideDecorator)''' - - @pyqtSlot("quint64", str) - def 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()) - - node.callDecoration("addSetting", key) - - @pyqtSlot("quint64", str) - 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) - - def _updateModel(self): - self.clear() - - for node in BreadthFirstIterator(self._root): - if type(node) is not SceneNode or not node.isSelectable(): - continue - - 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 8c9b63ac1b..502e903416 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2016 Ultimaker B.V. // Uranium is released under the terms of the AGPLv3 or higher. import QtQuick 2.2 @@ -18,12 +18,6 @@ Item { width: childrenRect.width; height: childrenRect.height; - - function updateContainerID() - { - console.log("containerid",UM.ActiveTool.properties.getValue("ContainerID")) - } - Column { id: items @@ -32,6 +26,99 @@ Item { spacing: UM.Theme.getSize("default_margin").height; + Row + { + ComboBox + { + id: extruderSelector + + model: Cura.ExtrudersModel + { + id: extruders_model + } + visible: extruders_model.rowCount() > 1 + textRole: "name" + width: items.width + height: UM.Theme.getSize("section").height + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: wheel.accepted = true; + } + + style: ComboBoxStyle + { + background: Rectangle + { + color: + { + if(extruderSelector.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_control_highlight"); + } + else + { + return extruders_model.getItem(extruderSelector.currentIndex).colour; + } + } + border.width: UM.Theme.getSize("default_lining").width + border.color: 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: extruderSelector.currentText + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("setting_control_disabled_text") + + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + UM.RecolorImage + { + id: downArrow + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.verticalCenter: parent.verticalCenter + + source: UM.Theme.getIcon("arrow_bottom") + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + 5 + sourceSize.height: width + 5 + + color: UM.Theme.getColor("setting_control_text") + } + } + } + + onActivated: UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id); + onModelChanged: updateCurrentIndex(); + + function updateCurrentIndex() + { + for(var i = 0; i < extruders_model.rowCount(); ++i) + { + if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder")) + { + extruderSelector.currentIndex = i; + return; + } + } + extruderSelector.currentIndex = -1; + } + } + } + Repeater { id: contents @@ -47,53 +134,91 @@ Item { } } - delegate: Loader + delegate: Row { - width: UM.Theme.getSize("setting").width; - height: UM.Theme.getSize("setting").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 differnt options. So disable asynchronous loading of enum type completely. - asynchronous: model.type != "enum" - - source: + Loader { - switch(model.type) // TODO: This needs to be fixed properly. Got frustrated with it not working, so this is the patch job! + id: settingLoader + 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" && model.type != "extruder" + + onLoaded: { + settingLoader.item.showRevertButton = false + settingLoader.item.showInheritButton = false + settingLoader.item.doDepthIndentation = false + } + + sourceComponent: { - 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" + switch(model.type) + { + case "int": + return settingTextField + case "float": + return settingTextField + case "enum": + return settingComboBox + case "extruder": + return settingExtruder + case "bool": + return settingCheckBox + case "str": + return settingTextField + case "category": + return settingCategory + default: + return settingUnknown + } } } + Button + { + width: UM.Theme.getSize("setting").height; + height: UM.Theme.getSize("setting").height; + + onClicked: addedSettingsModel.setVisible(model.key, false); + + style: ButtonStyle + { + background: Rectangle + { + color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor; + UM.RecolorImage + { + 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" ] + watchedProperties: [ "value", "enabled", "validationState" ] storeIndex: 0 } } - } + Button { id: customise_settings_button; @@ -158,11 +283,11 @@ Item { { if(text != "") { - listview.model.filter = {"global_only": false, "label": "*" + text} + listview.model.filter = {"settable_per_mesh": true, "label": "*" + text} } else { - listview.model.filter = {"global_only": false} + listview.model.filter = {"settable_per_mesh": true} } } } @@ -187,7 +312,7 @@ Item { containerId: Cura.MachineManager.activeDefinitionId filter: { - "global_only": false + "settable_per_mesh": true } visibilityHandler: UM.SettingPreferenceVisibilityHandler {} } @@ -227,4 +352,46 @@ Item { } SystemPalette { id: palette; } + + Component + { + id: settingTextField; + + Cura.SettingTextField { } + } + + Component + { + id: settingComboBox; + + Cura.SettingComboBox { } + } + + Component + { + id: settingExtruder; + + Cura.SettingExtruder { } + } + + Component + { + id: settingCheckBox; + + Cura.SettingCheckBox { } + } + + Component + { + id: settingCategory; + + Cura.SettingCategory { } + } + + Component + { + id: settingUnknown; + + Cura.SettingUnknown { } + } } diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index c74800e83d..395dbdc594 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -1,19 +1,20 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2016 Ultimaker B.V. # Uranium is released under the terms of the AGPLv3 or higher. from UM.Tool import Tool from UM.Scene.Selection import Selection from UM.Application import Application from UM.Preferences import Preferences +from cura.SettingOverrideDecorator import SettingOverrideDecorator -from . import PerObjectSettingsModel - +## This tool allows the user to add & change settings per node in the scene. +# The settings per object are kept in a ContainerStack, which is linked to a node by decorator. class PerObjectSettingsTool(Tool): def __init__(self): super().__init__() self._model = None - self.setExposedProperties("SelectedObjectId","ContainerID") + self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder") Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged) Selection.selectionChanged.connect(self.propertyChanged) @@ -33,21 +34,31 @@ class PerObjectSettingsTool(Tool): return selected_object_id def getContainerID(self): + selected_object = Selection.getSelectedObject(0) + if selected_object.getParent().callDecoration("isGroup"): + selected_object = selected_object.getParent() 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 + return selected_object.callDecoration("getStack").getId() + except AttributeError: + return "" - def setContainerID(self, value): - pass + ## Gets the active extruder of the currently selected object. + # + # \return The active extruder of the currently selected object. + def getSelectedActiveExtruder(self): + selected_object = Selection.getSelectedObject(0) + selected_object.callDecoration("getActiveExtruder") + + ## Changes the active extruder of the currently selected object. + # + # \param extruder_stack_id The ID of the extruder to print the currently + # selected object with. + def setSelectedActiveExtruder(self, extruder_stack_id): + selected_object = Selection.getSelectedObject(0) + stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. + if not stack: + selected_object.addDecorator(SettingOverrideDecorator()) + selected_object.callDecoration("setActiveExtruder", extruder_stack_id) def _onPreferenceChanged(self, preference): if preference == "cura/active_mode": diff --git a/plugins/PerObjectSettingsTool/SettingOverrideModel.py b/plugins/PerObjectSettingsTool/SettingOverrideModel.py deleted file mode 100644 index d4bebfdfee..0000000000 --- a/plugins/PerObjectSettingsTool/SettingOverrideModel.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2015 Ultimaker B.V. -# Uranium is released under the terms of the AGPLv3 or higher. - -from PyQt5.QtCore import Qt, pyqtSlot, QUrl - -from UM.Application import Application -from UM.Qt.ListModel import ListModel -#from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator - -class SettingOverrideModel(ListModel): - KeyRole = Qt.UserRole + 1 - LabelRole = Qt.UserRole + 2 - DescriptionRole = Qt.UserRole + 3 - ValueRole = Qt.UserRole + 4 - TypeRole = Qt.UserRole + 5 - UnitRole = Qt.UserRole + 6 - ValidRole = Qt.UserRole + 7 - OptionsRole = Qt.UserRole + 8 - WarningDescriptionRole = Qt.UserRole + 9 - ErrorDescriptionRole = Qt.UserRole + 10 - GlobalOnlyRole = Qt.UserRole + 11 - - def __init__(self, node, parent = None): - super().__init__(parent) - - self._ignore_setting_change = None - - self._node = node - self._node.decoratorsChanged.connect(self._onDecoratorsChanged) - self._onDecoratorsChanged(None) - - #self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile() #To be able to get notified when a setting changes. - #self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged) - #Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onProfileChanged) - - self.addRoleName(self.KeyRole, "key") - self.addRoleName(self.LabelRole, "label") - self.addRoleName(self.DescriptionRole, "description") - self.addRoleName(self.ValueRole,"value") - self.addRoleName(self.TypeRole, "type") - self.addRoleName(self.UnitRole, "unit") - self.addRoleName(self.ValidRole, "valid") - self.addRoleName(self.OptionsRole, "options") - self.addRoleName(self.WarningDescriptionRole, "warning_description") - self.addRoleName(self.ErrorDescriptionRole, "error_description") - self.addRoleName(self.GlobalOnlyRole, "global_only") - - @pyqtSlot(str, "QVariant") - def setSettingValue(self, key, value): - if not self._decorator: - return - - self._decorator.setSettingValue(key, value) - - def _onDecoratorsChanged(self, node): - return - '''if not self._node.getDecorator(SettingOverrideDecorator): - self.clear() - return - - self._decorator = self._node.getDecorator(SettingOverrideDecorator) - self._decorator.settingAdded.connect(self._onSettingsChanged) - self._decorator.settingRemoved.connect(self._onSettingsChanged) - self._decorator.settingValueChanged.connect(self._onSettingValueChanged) - self._onSettingsChanged()''' - - def _createOptionsModel(self, options): - if not options: - return None - - model = ListModel() - model.addRoleName(Qt.UserRole + 1, "value") - model.addRoleName(Qt.UserRole + 2, "name") - for value, name in options.items(): - model.appendItem({"value": str(value), "name": str(name)}) - return model - - ## Updates the active profile in this model if the active profile is - # changed. - # - # This links the settingValueChanged of the new profile to this model's - # _onSettingValueChanged function, so that it properly listens to those - # events again. - def _onProfileChanged(self): - if self._activeProfile: #Unlink from the old profile. - self._activeProfile.settingValueChanged.disconnect(self._onProfileSettingValueChanged) - old_profile = self._activeProfile - self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile() - self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged) #Re-link to the new profile. - for setting_name in old_profile.getChangedSettings().keys(): #Update all changed settings in the old and new profiles. - self._onProfileSettingValueChanged(setting_name) - for setting_name in self._activeProfile.getChangedSettings().keys(): - self._onProfileSettingValueChanged(setting_name) - - ## Updates the global_only property of a setting once a setting value - # changes. - # - # This method should only get called on settings that are dependent on the - # changed setting. - # - # \param setting_name The setting that needs to be updated. - def _onProfileSettingValueChanged(self, setting_name): - index = self.find("key", setting_name) - if index != -1: - self.setProperty(index, "global_only", Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getSetting(setting_name).getGlobalOnly()) - - def _onSettingsChanged(self): - self.clear() - - items = [] - for key, setting in self._decorator.getAllSettings().items(): - value = self._decorator.getSettingValue(key) - items.append({ - "key": key, - "label": setting.getLabel(), - "description": setting.getDescription(), - "value": str(value), - "type": setting.getType(), - "unit": setting.getUnit(), - "valid": setting.validate(value), - "options": self._createOptionsModel(setting.getOptions()), - "warning_description": setting.getWarningDescription(), - "error_description": setting.getErrorDescription(), - "global_only": setting.getGlobalOnly() - }) - - items.sort(key = lambda i: i["key"]) - - for item in items: - self.appendItem(item) - - def _onSettingValueChanged(self, setting): - index = self.find("key", setting.getKey()) - value = self._decorator.getSettingValue(setting.getKey()) - if index != -1: - self.setProperty(index, "value", str(value)) - self.setProperty(index, "valid", setting.validate(value)) - self.setProperty(index, "global_only", setting.getGlobalOnly()) \ No newline at end of file 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/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index d73beec193..89c7d76e9f 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -45,6 +45,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): self.addMetaDataEntry("material", material.text) self.addMetaDataEntry("color_name", color.text) + continue + self.addMetaDataEntry(tag_name, entry.text) property_values = {} @@ -78,6 +80,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): if key in self.__material_property_setting_map: self.setProperty(self.__material_property_setting_map[key], "value", entry.text, self._definition) global_setting_values[self.__material_property_setting_map[key]] = entry.text + else: + Logger.log("d", "Unsupported material setting %s", key) machines = data.iterfind("./um:settings/um:machine", self.__namespaces) for machine in machines: @@ -87,6 +91,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): key = entry.get("key") if key in self.__material_property_setting_map: machine_setting_values[self.__material_property_setting_map[key]] = entry.text + else: + Logger.log("d", "Unsupported material setting %s", key) identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces) for identifier in identifiers: @@ -100,28 +106,74 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): Logger.log("w", "No definition found for machine ID %s", machine_id) continue + definition = definitions[0] + new_material = XmlMaterialProfile(self.id + "_" + machine_id) new_material.setName(self.getName()) - new_material.setMetaData(self.getMetaData()) - new_material.setDefinition(definitions[0]) + new_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_material.setDefinition(definition) for key, value in global_setting_values.items(): - new_material.setProperty(key, "value", value, definitions[0]) + new_material.setProperty(key, "value", value, definition) for key, value in machine_setting_values.items(): - new_material.setProperty(key, "value", value, definitions[0]) + new_material.setProperty(key, "value", value, definition) new_material._dirty = False UM.Settings.ContainerRegistry.getInstance().addContainer(new_material) + hotends = machine.iterfind("./um:hotend", self.__namespaces) + for hotend in hotends: + hotend_id = hotend.get("id") + if hotend_id is None: + continue + variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id) + if not variant_containers: + # It is not really properly defined what "ID" is so also search for variants by name. + variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id) + + if not variant_containers: + Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id) + continue + + new_hotend_material = XmlMaterialProfile(self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")) + new_hotend_material.setName(self.getName()) + new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_hotend_material.setDefinition(definition) + + new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id) + + for key, value in global_setting_values.items(): + new_hotend_material.setProperty(key, "value", value, definition) + + for key, value in machine_setting_values.items(): + new_hotend_material.setProperty(key, "value", value, definition) + + settings = hotend.iterfind("./um:setting", self.__namespaces) + for entry in settings: + key = entry.get("key") + if key in self.__material_property_setting_map: + new_hotend_material.setProperty(self.__material_property_setting_map[key], "value", entry.text, definition) + else: + Logger.log("d", "Unsupported material setting %s", key) + + new_hotend_material._dirty = False + UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material) + + + # Map XML file setting names to internal names __material_property_setting_map = { "print temperature": "material_print_temperature", "heated bed temperature": "material_bed_temperature", "standby temperature": "material_standby_temperature", + "print cooling": "cool_fan_speed", + "retraction amount": "retraction_amount", + "retraction speed": "retraction_speed", } + # Map XML file product names to internal ids __product_id_map = { "Ultimaker2": "ultimaker2", "Ultimaker2+": "ultimaker2_plus", diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index f755d72c4a..940b0bb3ed 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -22,9 +22,13 @@ { "label": "Extruder", "description": "The extruder train used for printing. This is used in multi-extrusion.", - "type": "int", + "type": "extruder", "default_value": 0, - "minimum_value": "0" + "minimum_value": "0", + "settable_per_mesh": true, + "settable_per_extruder": false, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_nozzle_offset_x": { @@ -33,7 +37,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_nozzle_offset_y": { @@ -42,7 +49,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_start_code": { @@ -50,7 +60,10 @@ "description": "Start g-code to execute whenever turning the extruder on.", "type": "str", "default_value": "", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_start_pos_abs": { @@ -58,7 +71,10 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_start_pos_x": { @@ -67,7 +83,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_start_pos_y": { @@ -76,7 +95,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_end_code": { @@ -84,7 +106,10 @@ "description": "End g-code to execute whenever turning the extruder off.", "type": "str", "default_value": "", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_end_pos_abs": { @@ -92,7 +117,10 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_end_pos_x": { @@ -101,7 +129,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "machine_extruder_end_pos_y": { @@ -110,7 +141,10 @@ "type": "float", "unit": "mm", "default_value": 0, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false } } } diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6828b8dd89..5c1d45442b 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -11,11 +11,11 @@ "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" + "preferred_quality": "normal", + "machine_extruder_trains": + { + "0": "fdmextruder" + } }, "settings": { @@ -24,6 +24,7 @@ "label": "Machine", "type": "category", "description": "Machine specific settings", + "icon": "category_machine", "children": { "machine_show_variants": @@ -31,151 +32,189 @@ "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" + "label": "Show machine variants", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "type": "str", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "type": "str", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "type": "bool", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "label": "Wait for material heatup", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_width": { "description": "The width (X-direction) of the printable area.", "default_value": 100, - "global_only": true, "type": "float", - "label": "Machine width" + "label": "Machine width", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_depth": { "description": "The depth (Y-direction) of the printable area.", "default_value": 100, - "global_only": true, "type": "float", - "label": "Machine depth" + "label": "Machine depth", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_height": { "description": "The height (Z-direction) of the printable area.", "default_value": 100, - "global_only": true, "type": "float", - "label": "Machine height" + "label": "Machine height", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_heated_bed": { "description": "Whether the machine has a heated bed present.", "default_value": false, - "global_only": true, "label": "Has heated bed", - "type": "bool" + "type": "bool", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "label": "Is center origin", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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": "bool", - "label": "Number extruders" + "type": "int", + "label": "Number extruders", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "type": "float", + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false, + "settable_globally": false }, "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" + "label": "Nozzle length", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "label": "Nozzle angle", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "label": "Heat zone length", + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false }, "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" + "label": "Heat up speed", + "settable_per_mesh": false, + "settable_per_extruder": 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_value": 2.0, - "global_only": true, "type": "float", - "label": "Cool down speed" + "label": "Cool down speed", + "settable_per_mesh": false, + "settable_per_extruder": true }, "machine_gcode_flavor": { "description": "The type of gcode to be generated.", "default_value": "RepRap", - "global_only": true, "type": "str", - "label": "Gcode flavour" + "label": "Gcode flavour", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "label": "Disallowed areas", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_head_polygon": { @@ -200,8 +239,10 @@ 1 ] ], - "global_only": true, - "label": "Machine head polygon" + "label": "Machine head polygon", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_head_with_fans_polygon": { @@ -226,16 +267,20 @@ -10 ] ], - "global_only": true, - "label": "Machine head & Fan polygon" + "label": "Machine head & Fan polygon", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "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" + "type": "float", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "machine_nozzle_size": { @@ -245,14 +290,19 @@ "type": "float", "default_value": 0.4, "minimum_value": "0.001", - "maximum_value_warning": "10" + "maximum_value_warning": "10", + "settable_per_mesh": false, + "settable_per_extruder": true }, "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 + "default_value": true, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false } } }, @@ -274,7 +324,8 @@ "minimum_value": "0.001", "minimum_value_warning": "0.04", "maximum_value_warning": "0.8 * machine_nozzle_size", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "layer_height_0": { @@ -286,7 +337,8 @@ "minimum_value": "0.001", "minimum_value_warning": "0.04", "maximum_value_warning": "0.8 * machine_nozzle_size", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "line_width": { @@ -299,6 +351,7 @@ "default_value": 0.4, "type": "float", "value": "machine_nozzle_size", + "settable_per_mesh": true, "children": { "wall_line_width": @@ -312,6 +365,7 @@ "value":"line_width", "default_value": 0.4, "type": "float", + "settable_per_mesh": true, "children": { "wall_line_width_0": @@ -324,7 +378,8 @@ "maximum_value_warning": "5", "default_value": 0.4, "value":"wall_line_width", - "type": "float" + "type": "float", + "settable_per_mesh": true }, "wall_line_width_x": { @@ -336,7 +391,8 @@ "maximum_value_warning": "5", "default_value": 0.4, "value":"wall_line_width", - "type": "float" + "type": "float", + "settable_per_mesh": true } } }, @@ -350,7 +406,8 @@ "maximum_value_warning": "5", "default_value": 0.4, "type": "float", - "value": "line_width" + "value": "line_width", + "settable_per_mesh": true }, "infill_line_width": { @@ -362,7 +419,8 @@ "maximum_value_warning": "5", "default_value": 0.4, "type": "float", - "value": "line_width" + "value": "line_width", + "settable_per_mesh": true }, "skirt_line_width": { @@ -374,8 +432,9 @@ "maximum_value_warning": "5", "default_value": 0.4, "type": "float", - "global_only": true, - "value": "line_width" + "value": "line_width", + "settable_per_mesh": false, + "settable_per_extruder": true }, "support_line_width": { @@ -388,8 +447,9 @@ "default_value": 0.4, "type": "float", "enabled": "support_enable", - "global_only": true, - "value": "line_width" + "value": "line_width", + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_roof_line_width": { @@ -401,8 +461,9 @@ "maximum_value_warning": "machine_nozzle_size * 2", "type": "float", "enabled": "support_roof_enable", - "global_only": true, - "value": "line_width" + "value": "line_width", + "settable_per_mesh": false, + "settable_per_extruder": false }, "prime_tower_line_width": { @@ -416,7 +477,8 @@ "minimum_value": "0.0001", "minimum_value_warning": "0.2", "maximum_value_warning": "5", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } } @@ -440,6 +502,7 @@ "minimum_value_warning": "line_width", "maximum_value_warning": "5 * line_width", "type": "float", + "settable_per_mesh": true, "children": { "wall_line_count": @@ -449,7 +512,8 @@ "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)" + "value": "1 if magic_spiralize else max(1, round((wall_thickness - wall_line_width_0) / wall_line_width_x) + 1)", + "settable_per_mesh": true } } }, @@ -460,9 +524,9 @@ "unit": "mm", "default_value": 0.8, "minimum_value": "0", - "maximum_value": "5", "minimum_value_warning": "0.6", "type": "float", + "settable_per_mesh": true, "children": { "top_thickness": @@ -475,6 +539,7 @@ "maximum_value_warning": "100", "type": "float", "value": "top_bottom_thickness", + "settable_per_mesh": true, "children": { "top_layers": @@ -485,7 +550,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))" + "value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / layer_height, 4))", + "settable_per_mesh": true } } }, @@ -498,6 +564,7 @@ "minimum_value": "0", "type": "float", "value": "top_bottom_thickness", + "settable_per_mesh": true, "children": { "bottom_layers": @@ -507,7 +574,8 @@ "minimum_value": "0", "default_value": 6, "type": "int", - "value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / layer_height, 4))" + "value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / layer_height, 4))", + "settable_per_mesh": true } } } @@ -524,7 +592,8 @@ "concentric": "Concentric", "zigzag": "Zig Zag" }, - "default_value": "lines" + "default_value": "lines", + "settable_per_mesh": true }, "wall_0_inset": { @@ -535,14 +604,16 @@ "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" + "maximum_value_warning": "machine_nozzle_size", + "settable_per_mesh": true }, "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 + "default_value": false, + "settable_per_mesh": true }, "travel_compensate_overlapping_walls_enabled": { @@ -550,6 +621,7 @@ "description": "Compensate the flow for parts of a wall being printed where there is already a wall in place.", "type": "bool", "default_value": true, + "settable_per_mesh": true, "children": { "travel_compensate_overlapping_walls_0_enabled": { @@ -557,14 +629,16 @@ "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" + "value": "travel_compensate_overlapping_walls_enabled", + "settable_per_mesh": true }, "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" + "value": "travel_compensate_overlapping_walls_enabled", + "settable_per_mesh": true } } }, @@ -576,7 +650,8 @@ "type": "float", "minimum_value_warning": "-10", "maximum_value_warning": "10", - "default_value": 0 + "default_value": 0, + "settable_per_mesh": true }, "z_seam_type": { @@ -589,14 +664,16 @@ "shortest": "Shortest", "random": "Random" }, - "default_value": "shortest" + "default_value": "shortest", + "settable_per_mesh": true }, "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 + "default_value": true, + "settable_per_mesh": true } } }, @@ -617,6 +694,7 @@ "default_value": 20, "minimum_value": "0", "maximum_value_warning": "100", + "settable_per_mesh": true, "children": { "infill_line_distance": @@ -627,7 +705,8 @@ "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))" + "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))", + "settable_per_mesh": true } } }, @@ -645,7 +724,8 @@ "zigzag": "Zig Zag" }, "default_value": "grid", - "value": "'lines' if infill_sparse_density > 25 else 'grid'" + "value": "'lines' if infill_sparse_density > 25 else 'grid'", + "settable_per_mesh": true }, "infill_overlap": { @@ -658,6 +738,7 @@ "minimum_value_warning": "-50", "maximum_value_warning": "100", "enabled": "infill_pattern != 'concentric'", + "settable_per_mesh": true, "children": { "infill_overlap_mm": @@ -670,7 +751,8 @@ "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'" + "enabled": "infill_pattern != 'concentric'", + "settable_per_mesh": true } } }, @@ -684,6 +766,7 @@ "maximum_value_warning": "100", "value": "5 if top_bottom_pattern != 'concentric' else 0", "enabled": "top_bottom_pattern != 'concentric'", + "settable_per_mesh": true, "children": { "skin_overlap_mm": { "label": "Skin Overlap", @@ -694,7 +777,8 @@ "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'" + "enabled": "top_bottom_pattern != 'concentric'", + "settable_per_mesh": true } } }, @@ -707,7 +791,8 @@ "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" + "maximum_value_warning": "machine_nozzle_size", + "settable_per_mesh": true }, "infill_sparse_thickness": { @@ -719,14 +804,16 @@ "minimum_value": "0.0001", "maximum_value_warning": "0.32", "maximum_value": "layer_height * 8", - "value": "layer_height" + "value": "layer_height", + "settable_per_mesh": true }, "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 + "default_value": true, + "settable_per_mesh": true } } }, @@ -745,7 +832,8 @@ "type": "bool", "default_value": false, "enabled": "False", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_print_temperature": { @@ -756,7 +844,9 @@ "default_value": 210, "minimum_value": "0", "maximum_value_warning": "260", - "enabled": "not (material_flow_dependent_temperature)" + "enabled": "not (material_flow_dependent_temperature)", + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_flow_temp_graph": { @@ -767,7 +857,8 @@ "default_value": "[[3.5,200],[7.0,240]]", "enabled": "False", "comments": "old enabled function: material_flow_dependent_temperature", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_extrusion_cool_down_speed": { "label": "Extrusion Cool Down Speed Modifier", @@ -777,9 +868,10 @@ "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" + "comments": "old enabled function: material_flow_dependent_temperature or machine_extruder_count > 1", + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_bed_temperature": { "label": "Bed Temperature", @@ -790,7 +882,9 @@ "minimum_value": "0", "maximum_value_warning": "260", "enabled": "machine_heated_bed", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "material_diameter": { "label": "Diameter", @@ -801,7 +895,8 @@ "minimum_value": "0.0001", "minimum_value_warning": "0.4", "maximum_value_warning": "3.5", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_flow": { "label": "Flow", @@ -811,13 +906,15 @@ "type": "float", "minimum_value": "5", "minimum_value_warning": "50", - "maximum_value_warning": "150" + "maximum_value_warning": "150", + "settable_per_mesh": true }, "retraction_enable": { "label": "Enable Retraction", "description": "Retract the filament when the nozzle is moving over a non-printed area. ", "type": "bool", - "default_value": true + "default_value": true, + "settable_per_mesh": true }, "retraction_amount": { "label": "Retraction Distance", @@ -827,7 +924,8 @@ "default_value": 6.5, "minimum_value_warning": "-0.0001", "maximum_value_warning": "10.0", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "retraction_speed": { "label": "Retraction Speed", @@ -839,6 +937,7 @@ "maximum_value": "299792458000", "maximum_value_warning": "100", "enabled": "retraction_enable", + "settable_per_mesh": true, "children": { "retraction_retract_speed": { "label": "Retraction Retract Speed", @@ -850,7 +949,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "100", "enabled": "retraction_enable", - "value": "retraction_speed" + "value": "retraction_speed", + "settable_per_mesh": true }, "retraction_prime_speed": { "label": "Retraction Prime Speed", @@ -862,7 +962,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "100", "enabled": "retraction_enable", - "value": "retraction_speed" + "value": "retraction_speed", + "settable_per_mesh": true } } }, @@ -874,7 +975,8 @@ "default_value": 0, "minimum_value_warning": "-0.0001", "maximum_value_warning": "5.0", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "retraction_min_travel": { "label": "Retraction Minimum Travel", @@ -885,16 +987,18 @@ "value": "line_width * 2", "minimum_value": "0", "maximum_value_warning": "10", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "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, + "default_value": 90, "minimum_value": "0", "maximum_value_warning": "100", "type": "int", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "retraction_extrusion_window": { "label": "Minimum Extrusion Distance Window", @@ -905,7 +1009,8 @@ "minimum_value": "0", "maximum_value_warning": "retraction_amount * 2", "value": "retraction_amount", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "retraction_hop": { "label": "Z Hop when Retracting", @@ -915,7 +1020,8 @@ "default_value": 0, "minimum_value_warning": "-0.0001", "maximum_value_warning": "10", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": true }, "material_standby_temperature": { @@ -926,7 +1032,8 @@ "default_value": 150, "minimum_value": "0", "maximum_value_warning": "260", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "switch_extruder_retraction_amount": { @@ -939,7 +1046,8 @@ "value": "machine_heat_zone_length", "minimum_value_warning": "0", "maximum_value_warning": "100", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "switch_extruder_retraction_speeds": { @@ -951,7 +1059,8 @@ "default_value": 20, "minimum_value": "0.1", "maximum_value_warning": "300", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "switch_extruder_retraction_speed": @@ -965,7 +1074,8 @@ "value": "switch_extruder_retraction_speeds", "minimum_value": "0.1", "maximum_value_warning": "300", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "switch_extruder_prime_speed": { @@ -978,7 +1088,8 @@ "value": "switch_extruder_retraction_speeds", "minimum_value": "0.1", "maximum_value_warning": "300", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -991,7 +1102,9 @@ "default_value": 1, "minimum_value_warning": "-0.0001", "maximum_value_warning": "10", - "enabled": "retraction_enable" + "enabled": "retraction_enable", + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1013,6 +1126,7 @@ "maximum_value_warning": "150", "maximum_value": "299792458000", "default_value": 60, + "settable_per_mesh": true, "children": { "speed_infill": @@ -1025,7 +1139,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "default_value": 60, - "value": "speed_print" + "value": "speed_print", + "settable_per_mesh": true }, "speed_wall": { @@ -1038,6 +1153,7 @@ "maximum_value_warning": "150", "default_value": 30, "value": "speed_print / 2", + "settable_per_mesh": true, "children": { "speed_wall_0": @@ -1050,7 +1166,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "default_value": 30, - "value": "speed_wall" + "value": "speed_wall", + "settable_per_mesh": true }, "speed_wall_x": { @@ -1062,7 +1179,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "default_value": 60, - "value": "speed_wall * 2" + "value": "speed_wall * 2", + "settable_per_mesh": true } } }, @@ -1076,7 +1194,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "default_value": 30, - "value": "speed_print / 2" + "value": "speed_print / 2", + "settable_per_mesh": true }, "speed_support": { @@ -1090,6 +1209,8 @@ "default_value": 60, "value": "speed_print", "enabled": "support_roof_enable", + "settable_per_mesh": false, + "settable_per_extruder": false, "children": { "speed_support_infill": @@ -1104,7 +1225,8 @@ "maximum_value_warning": "150", "value": "speed_support", "enabled": "support_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "speed_support_roof": { @@ -1116,9 +1238,10 @@ "minimum_value": "0.1", "maximum_value": "299792458000", "maximum_value_warning": "150", - "enabled": "support_roof_enable", + "enabled": "support_roof_enable and support_enable", "value": "speed_support / 1.5", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false } } }, @@ -1133,7 +1256,8 @@ "value": "speed_print", "minimum_value": "0.1", "maximum_value_warning": "150", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1148,7 +1272,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "300", "value": "speed_print if magic_spiralize else 120", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "speed_layer_0": { "label": "Initial Layer Speed", @@ -1158,7 +1283,8 @@ "default_value": 30, "minimum_value": "0.1", "maximum_value": "299792458000", - "maximum_value_warning": "300" + "maximum_value_warning": "300", + "settable_per_mesh": true }, "skirt_speed": { "label": "Skirt Speed", @@ -1170,7 +1296,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "300", "value": "speed_layer_0", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "speed_slowdown_layers": { @@ -1181,7 +1308,8 @@ "minimum_value": "0", "maximum_value": "299792458000", "maximum_value_warning": "300", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, @@ -1537,7 +1665,7 @@ "noskin": "No Skin" }, "default_value": "all", - "global_only": true + "settable_per_mesh": true }, "travel_avoid_other_parts": { @@ -1546,7 +1674,8 @@ "type": "bool", "default_value": true, "enabled": "retraction_combing != \"off\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "travel_avoid_distance": { @@ -1559,7 +1688,8 @@ "minimum_value": "0", "maximum_value_warning": "machine_nozzle_tip_outer_diameter * 5", "enabled": "retraction_combing != \"off\" and travel_avoid_other_parts", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1577,7 +1707,8 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": true }, "cool_fan_speed": { @@ -1590,7 +1721,8 @@ "default_value": 100, "value": "100.0 if cool_fan_enabled else 0.0", "enabled": "cool_fan_enabled", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "cool_fan_speed_min": @@ -1604,7 +1736,8 @@ "value": "cool_fan_speed", "default_value": 100, "enabled": "cool_fan_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "cool_fan_speed_max": { @@ -1616,8 +1749,9 @@ "maximum_value": "100", "default_value": 100, "enabled": "cool_fan_enabled", - "global_only": "True", - "value": "cool_fan_speed" + "value": "cool_fan_speed", + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1630,7 +1764,8 @@ "default_value": 10, "minimum_value": "cool_min_layer_time", "maximum_value_warning": "600", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "cool_fan_full_at_height": { @@ -1642,7 +1777,8 @@ "value": "layer_height_0", "minimum_value": "0", "maximum_value_warning": "10.0", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "cool_fan_full_layer": @@ -1654,7 +1790,8 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1667,7 +1804,8 @@ "default_value": 5, "minimum_value": "0", "maximum_value_warning": "600", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "cool_min_speed": { @@ -1678,7 +1816,8 @@ "default_value": 10, "minimum_value": "0", "maximum_value_warning": "100", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "cool_lift_head": { @@ -1686,7 +1825,8 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -1703,7 +1843,8 @@ "label": "Enable Support", "description": "Enable support structures. These structures support parts of the model with severe overhangs.", "type": "bool", - "default_value": false + "default_value": false, + "settable_per_mesh": true }, "support_type": { @@ -1716,7 +1857,9 @@ "everywhere": "Everywhere" }, "default_value": "everywhere", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_angle": { @@ -1727,7 +1870,8 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 50, - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_pattern": { @@ -1744,7 +1888,8 @@ }, "default_value": "zigzag", "enabled": "support_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_connect_zigzags": { @@ -1753,7 +1898,8 @@ "type": "bool", "default_value": true, "enabled": "support_enable and (support_pattern == \"zigzag\")", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_infill_rate": { @@ -1765,7 +1911,8 @@ "maximum_value_warning": "100", "default_value": 15, "enabled": "support_enable", - "global_only": true, + "settable_per_mesh": false, + "settable_per_extruder": false, "children": { "support_line_distance": { @@ -1777,7 +1924,8 @@ "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 + "settable_per_mesh": false, + "settable_per_extruder": false } } }, @@ -1791,7 +1939,7 @@ "maximum_value_warning": "10", "default_value": 0.15, "enabled": "support_enable", - + "settable_per_mesh": true, "children": { "support_top_distance": @@ -1804,7 +1952,8 @@ "default_value": 0.15, "type": "float", "enabled": "support_enable", - "value": "support_z_distance" + "value": "support_z_distance", + "settable_per_mesh": true }, "support_bottom_distance": { @@ -1816,7 +1965,8 @@ "default_value": 0.1, "value": "0.1 if support_type == 'everywhere' else 0", "type": "float", - "enabled": "support_enable and support_type == 'everywhere'" + "enabled": "support_enable and support_type == 'everywhere'", + "settable_per_mesh": true } } }, @@ -1829,7 +1979,8 @@ "minimum_value": "0", "maximum_value_warning": "10", "default_value": 0.7, - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_xy_overrides_z": { "label": "Support Distance Priority", @@ -1840,7 +1991,8 @@ "z_overrides_xy": "Z overrides X/Y" }, "default_value": "z_overrides_xy", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_xy_distance_overhang": { "label": "Minimum Support X/Y Distance", @@ -1851,7 +2003,8 @@ "maximum_value_warning": "10", "default_value": 0.2, "value": "machine_nozzle_size / 2", - "enabled": "support_enable and support_xy_overrides_z=='z_overrides_xy'" + "enabled": "support_enable and support_xy_overrides_z=='z_overrides_xy'", + "settable_per_mesh": true }, "support_bottom_stair_step_height": { @@ -1862,7 +2015,8 @@ "default_value": 0.3, "minimum_value": "0", "maximum_value_warning": "1.0", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_join_distance": { @@ -1873,7 +2027,8 @@ "default_value": 2.0, "minimum_value_warning": "0", "maximum_value_warning": "10", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_offset": { @@ -1884,7 +2039,8 @@ "default_value": 0.2, "minimum_value_warning": "-0.5", "maximum_value_warning": "5.0", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_area_smoothing": { @@ -1895,7 +2051,8 @@ "default_value": 0.6, "minimum_value": "0", "maximum_value_warning": "1.0", - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_roof_enable": { @@ -1903,7 +2060,8 @@ "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" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_roof_height": { @@ -1914,7 +2072,8 @@ "default_value": 1, "minimum_value": "0", "maximum_value_warning": "10", - "enabled": "support_roof_enable" + "enabled": "support_roof_enable", + "settable_per_mesh": true }, "support_roof_density": { @@ -1926,7 +2085,8 @@ "minimum_value": "0", "maximum_value_warning": "100", "enabled":"support_roof_enable", - "global_only": true, + "settable_per_mesh": false, + "settable_per_extruder": false, "children": { "support_roof_line_distance": @@ -1939,7 +2099,8 @@ "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 + "settable_per_mesh": false, + "settable_per_extruder": false } } }, @@ -1958,7 +2119,8 @@ }, "default_value": "concentric", "enabled": "support_roof_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_use_towers": { @@ -1966,7 +2128,8 @@ "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" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_tower_diameter": { @@ -1977,7 +2140,8 @@ "default_value": 3.0, "minimum_value": "0", "maximum_value_warning": "10", - "enabled": "support_enable and support_use_towers" + "enabled": "support_enable and support_use_towers", + "settable_per_mesh": true }, "support_minimal_diameter": { @@ -1989,7 +2153,8 @@ "minimum_value": "0", "maximum_value_warning": "10", "maximum_value": "support_tower_diameter", - "enabled": "support_enable and support_use_towers" + "enabled": "support_enable and support_use_towers", + "settable_per_mesh": true }, "support_tower_roof_angle": { @@ -2000,7 +2165,8 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 65, - "enabled": "support_enable and support_use_towers" + "enabled": "support_enable and support_use_towers", + "settable_per_mesh": true } } }, @@ -2024,7 +2190,8 @@ "raft": "Raft" }, "default_value": "brim", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "skirt_line_count": { @@ -2035,7 +2202,8 @@ "minimum_value": "0", "maximum_value_warning": "10", "enabled": "adhesion_type == \"skirt\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "skirt_gap": { @@ -2047,7 +2215,8 @@ "minimum_value_warning": "0", "maximum_value_warning": "100", "enabled": "adhesion_type == \"skirt\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "skirt_minimal_length": { @@ -2060,7 +2229,8 @@ "minimum_value_warning": "25", "maximum_value_warning": "2500", "enabled": "adhesion_type == \"skirt\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "brim_width": { @@ -2072,7 +2242,8 @@ "minimum_value": "0.0", "maximum_value_warning": "100.0", "enabled": "adhesion_type == \"brim\"", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "brim_line_count": @@ -2085,7 +2256,8 @@ "maximum_value_warning": "300", "value": "math.ceil(brim_width / skirt_line_width)", "enabled": "adhesion_type == \"brim\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -2098,8 +2270,7 @@ "default_value": 5, "minimum_value_warning": "0", "maximum_value_warning": "10", - "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "enabled": "adhesion_type == \"raft\"" }, "raft_airgap": { @@ -2111,7 +2282,8 @@ "minimum_value": "0", "maximum_value_warning": "1.0", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "layer_0_z_overlap": { "label": "Initial Layer Z Overlap", @@ -2123,7 +2295,8 @@ "minimum_value": "0", "maximum_value_warning": "layer_height", "enabled": "adhesion_type == 'raft'", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_surface_layers": { @@ -2134,7 +2307,8 @@ "minimum_value": "0", "maximum_value_warning": "20", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_surface_thickness": { @@ -2146,7 +2320,8 @@ "minimum_value": "0", "maximum_value_warning": "2.0", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_surface_line_width": { @@ -2158,7 +2333,8 @@ "minimum_value": "0.0001", "maximum_value_warning": "machine_nozzle_size * 2", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_surface_line_spacing": { @@ -2171,7 +2347,8 @@ "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", "value": "raft_surface_line_width", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_interface_thickness": { @@ -2183,7 +2360,8 @@ "minimum_value": "0", "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_interface_line_width": { @@ -2192,11 +2370,12 @@ "unit": "mm", "type": "float", "default_value": 1, - "value": "line_width", + "value": "line_width * 2", "minimum_value": "0.0001", "maximum_value_warning": "machine_nozzle_size * 2", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_interface_line_spacing": { @@ -2208,7 +2387,8 @@ "minimum_value": "0", "maximum_value_warning": "15.0", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_base_thickness": { @@ -2220,7 +2400,8 @@ "minimum_value": "0", "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_base_line_width": { @@ -2230,10 +2411,11 @@ "type": "float", "default_value": 1, "minimum_value": "0.0001", - "value": "line_width", + "value": "line_width * 2", "maximum_value_warning": "machine_nozzle_size * 2", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_base_line_spacing": { @@ -2245,7 +2427,8 @@ "minimum_value": "0.0001", "maximum_value_warning": "100", "enabled": "adhesion_type == \"raft\"", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_speed": { @@ -2259,7 +2442,8 @@ "maximum_value_warning": "200", "enabled": "adhesion_type == \"raft\"", "value": "speed_print / 60 * 30", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "raft_surface_speed": @@ -2274,7 +2458,8 @@ "maximum_value_warning": "100", "enabled": "adhesion_type == \"raft\"", "value": "raft_speed", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_interface_speed": { @@ -2288,7 +2473,8 @@ "maximum_value_warning": "150", "enabled": "adhesion_type == \"raft\"", "value": "0.5 * raft_speed", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_base_speed": { @@ -2302,7 +2488,8 @@ "maximum_value_warning": "200", "enabled": "adhesion_type == \"raft\"", "value": "0.5 * raft_speed", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -2424,7 +2611,8 @@ "minimum_value": "0", "maximum_value": "100", "default_value": 0, - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": true, "enabled": "adhesion_type == \"raft\"", "children": { @@ -2437,9 +2625,10 @@ "minimum_value": "0", "maximum_value": "100", "default_value": 0, - "global_only": "True", "value": "raft_fan_speed", - "enabled": "adhesion_type == \"raft\"" + "enabled": "adhesion_type == \"raft\"", + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_interface_fan_speed": { @@ -2450,9 +2639,10 @@ "minimum_value": "0", "maximum_value": "100", "default_value": 0, - "global_only": "True", "value": "raft_fan_speed", - "enabled": "adhesion_type == \"raft\"" + "enabled": "adhesion_type == \"raft\"", + "settable_per_mesh": false, + "settable_per_extruder": true }, "raft_base_fan_speed": { @@ -2463,9 +2653,10 @@ "minimum_value": "0", "maximum_value": "100", "default_value": 0, - "global_only": "True", "value": "raft_fan_speed", - "enabled": "adhesion_type == \"raft\"" + "enabled": "adhesion_type == \"raft\"", + "settable_per_mesh": false, + "settable_per_extruder": true } } } @@ -2483,54 +2674,60 @@ { "label": "Platform Adhesion Extruder", "description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.", - "type": "int", + "type": "extruder", "default_value": 0, "minimum_value": "0", "maximum_value": "machine_extruder_count - 1", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "support_extruder_nr": { "label": "Support Extruder", "description": "The extruder train to use for printing the support. This is used in multi-extrusion.", - "type": "int", + "type": "extruder", "default_value": 0, "minimum_value": "0", "maximum_value": "machine_extruder_count - 1", - "global_only": "True", + "enabled": "support_enable", + "settable_per_mesh": false, + "settable_per_extruder": false, "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", + "type": "extruder", "default_value": 0, "value": "support_extruder_nr", "minimum_value": "0", "maximum_value": "machine_extruder_count - 1", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "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", + "type": "extruder", "default_value": 0, "value": "support_extruder_nr", "minimum_value": "0", "maximum_value": "machine_extruder_count - 1", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "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", + "type": "extruder", "default_value": 0, "value": "support_extruder_nr", "minimum_value": "0", "maximum_value": "machine_extruder_count - 1", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false } } }, @@ -2540,7 +2737,8 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": false }, "prime_tower_size": { @@ -2553,7 +2751,8 @@ "value": "15 if prime_tower_enable else 0", "minimum_value": "0", "maximum_value_warning": "20", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "prime_tower_position_x": { @@ -2565,7 +2764,8 @@ "default_value": 200, "minimum_value_warning": "-1000", "maximum_value_warning": "1000", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "prime_tower_position_y": { @@ -2577,7 +2777,8 @@ "default_value": 200, "minimum_value_warning": "-1000", "maximum_value_warning": "1000", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "prime_tower_flow": { @@ -2590,7 +2791,8 @@ "minimum_value": "0.0001", "minimum_value_warning": "50", "maximum_value_warning": "150", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": true }, "prime_tower_wipe_enabled": { @@ -2599,7 +2801,8 @@ "type": "bool", "enabled": "prime_tower_enable", "default_value": false, - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "multiple_mesh_overlap": { @@ -2609,7 +2812,8 @@ "unit": "mm", "default_value": 0.15, "minimum_value": "0", - "maximum_value_warning": "1.0" + "maximum_value_warning": "1.0", + "settable_per_mesh": true }, "ooze_shield_enabled": { @@ -2617,7 +2821,8 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": false }, "ooze_shield_angle": { @@ -2629,7 +2834,8 @@ "default_value": 60, "minimum_value": "0", "maximum_value": "90", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false }, "ooze_shield_dist": { @@ -2641,7 +2847,8 @@ "default_value": 2, "minimum_value": "0", "maximum_value_warning": "30", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false } } }, @@ -2658,28 +2865,32 @@ "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 + "default_value": true, + "settable_per_mesh": 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 + "default_value": false, + "settable_per_mesh": true }, "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 + "default_value": false, + "settable_per_mesh": true }, "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 + "default_value": false, + "settable_per_mesh": true } } }, @@ -2702,7 +2913,9 @@ "one_at_a_time": "One at a Time" }, "default_value": "all_at_once", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "magic_mesh_surface_mode": { @@ -2715,7 +2928,8 @@ "surface": "Surface", "both": "Both" }, - "default_value": "normal" + "default_value": "normal", + "settable_per_mesh": true }, "magic_spiralize": { @@ -2723,7 +2937,7 @@ "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" + "settable_per_mesh": true } } }, @@ -2741,7 +2955,8 @@ "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 + "settable_per_mesh": false, + "settable_per_extruder": false }, "draft_shield_dist": { @@ -2753,7 +2968,8 @@ "maximum_value_warning": "100", "default_value": 10, "enabled": "draft_shield_enabled", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "draft_shield_height_limitation": { @@ -2767,7 +2983,8 @@ }, "default_value": "full", "enabled": "draft_shield_enabled", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": false }, "draft_shield_height": { @@ -2780,7 +2997,8 @@ "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 + "settable_per_mesh": false, + "settable_per_extruder": false }, "conical_overhang_enabled": { "label": "Make Overhang Printable", @@ -2804,7 +3022,8 @@ "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 + "settable_per_mesh": false, + "settable_per_extruder": true }, "coasting_volume": { @@ -2816,7 +3035,8 @@ "minimum_value": "0", "maximum_value_warning": "2.0", "enabled": "coasting_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "coasting_min_volume": { @@ -2828,7 +3048,8 @@ "minimum_value": "0", "maximum_value_warning": "10.0", "enabled": "coasting_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "coasting_speed": { @@ -2840,7 +3061,8 @@ "minimum_value": "0.0001", "maximum_value_warning": "100", "enabled": "coasting_enable", - "global_only": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "skin_outline_count": { @@ -2849,7 +3071,8 @@ "default_value": 0, "minimum_value": "0", "maximum_value_warning": "10", - "type": "int" + "type": "int", + "settable_per_mesh": true }, "skin_alternate_rotation": { @@ -2857,7 +3080,8 @@ "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\"" + "enabled": "top_bottom_pattern != \"concentric\"", + "settable_per_mesh": true }, "support_conical_enabled": { @@ -2865,7 +3089,8 @@ "description": "Experimental feature: Make support areas smaller at the bottom than at the overhang.", "type": "bool", "default_value": false, - "enabled": "support_enable" + "enabled": "support_enable", + "settable_per_mesh": true }, "support_conical_angle": { @@ -2878,7 +3103,8 @@ "maximum_value_warning": "45", "maximum_value": "90", "default_value": 30, - "enabled": "support_conical_enabled and support_enable" + "enabled": "support_conical_enabled and support_enable", + "settable_per_mesh": true }, "support_conical_min_width": { @@ -2890,14 +3116,16 @@ "minimum_value_warning": "machine_nozzle_size * 3", "maximum_value_warning": "100.0", "type": "float", - "enabled": "support_conical_enabled and support_enable" + "enabled": "support_conical_enabled and support_enable", + "settable_per_mesh": true }, "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 + "default_value": false, + "settable_per_mesh": true }, "magic_fuzzy_skin_thickness": { @@ -2908,7 +3136,8 @@ "default_value": 0.3, "minimum_value": "0.001", "maximum_value_warning": "wall_line_width_0", - "enabled": "magic_fuzzy_skin_enabled" + "enabled": "magic_fuzzy_skin_enabled", + "settable_per_mesh": true }, "magic_fuzzy_skin_point_density": { @@ -2922,6 +3151,7 @@ "maximum_value_warning": "10", "maximum_value": "2 / magic_fuzzy_skin_thickness", "enabled": "magic_fuzzy_skin_enabled", + "settable_per_mesh": true, "children": { "magic_fuzzy_skin_point_dist": @@ -2935,7 +3165,8 @@ "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" + "enabled": "magic_fuzzy_skin_enabled", + "settable_per_mesh": true } } }, @@ -2945,7 +3176,9 @@ "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" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_height": { @@ -2957,7 +3190,9 @@ "minimum_value": "0.0001", "maximum_value_warning": "20", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_roof_inset": { @@ -2971,7 +3206,9 @@ "maximum_value_warning": "20", "enabled": "wireframe_enabled", "value": "wireframe_height", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_printspeed": { @@ -2984,7 +3221,9 @@ "maximum_value": "299792458000", "maximum_value_warning": "50", "enabled": "wireframe_enabled", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false, "children": { "wireframe_printspeed_bottom": @@ -2998,8 +3237,10 @@ "maximum_value": "299792458000", "maximum_value_warning": "50", "enabled": "wireframe_enabled", - "global_only": "True", - "value": "wireframe_printspeed" + "value": "wireframe_printspeed", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_printspeed_up": { @@ -3012,8 +3253,10 @@ "maximum_value": "299792458000", "maximum_value_warning": "50", "enabled": "wireframe_enabled", - "global_only": "True", - "value": "wireframe_printspeed" + "value": "wireframe_printspeed", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_printspeed_down": { @@ -3026,8 +3269,10 @@ "maximum_value": "299792458000", "maximum_value_warning": "50", "enabled": "wireframe_enabled", - "global_only": "True", - "value": "wireframe_printspeed" + "value": "wireframe_printspeed", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_printspeed_flat": { @@ -3041,7 +3286,9 @@ "maximum_value_warning": "100", "value": "wireframe_printspeed", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false } } }, @@ -3055,7 +3302,9 @@ "maximum_value_warning": "100", "type": "float", "enabled": "wireframe_enabled", - "global_only": "True", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false, "children": { "wireframe_flow_connection": @@ -3068,8 +3317,10 @@ "maximum_value_warning": "100", "type": "float", "enabled": "wireframe_enabled", - "global_only": "True", - "value": "wireframe_flow" + "value": "wireframe_flow", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_flow_flat": { @@ -3081,8 +3332,10 @@ "maximum_value_warning": "100", "type": "float", "enabled": "wireframe_enabled", - "global_only": "True", - "value": "wireframe_flow" + "value": "wireframe_flow", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false } } }, @@ -3096,7 +3349,9 @@ "minimum_value": "0", "maximum_value_warning": "1", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_bottom_delay": { @@ -3108,7 +3363,9 @@ "minimum_value": "0", "maximum_value_warning": "1", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_flat_delay": { @@ -3120,7 +3377,9 @@ "minimum_value": "0", "maximum_value_warning": "0.5", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_up_half_speed": { @@ -3132,7 +3391,9 @@ "minimum_value": "0", "maximum_value_warning": "5.0", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_top_jump": { @@ -3144,7 +3405,9 @@ "minimum_value": "0", "maximum_value_warning": "2.0", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_fall_down": { @@ -3156,7 +3419,9 @@ "minimum_value": "0", "maximum_value_warning": "wireframe_height", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_drag_along": { @@ -3168,7 +3433,9 @@ "minimum_value": "0", "maximum_value_warning": "wireframe_height", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_strategy": { @@ -3183,7 +3450,9 @@ }, "default_value": "compensate", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_straight_before_down": { @@ -3195,7 +3464,9 @@ "minimum_value": "0", "maximum_value": "100", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_roof_fall_down": { @@ -3207,7 +3478,9 @@ "minimum_value_warning": "0", "maximum_value_warning": "wireframe_roof_inset", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_roof_drag_along": { @@ -3219,7 +3492,9 @@ "minimum_value": "0", "maximum_value_warning": "10", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_roof_outer_delay": { @@ -3231,7 +3506,9 @@ "minimum_value": "0", "maximum_value_warning": "2.0", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false }, "wireframe_nozzle_clearance": { @@ -3243,7 +3520,9 @@ "minimum_value_warning": "0", "maximum_value_warning": "10.0", "enabled": "wireframe_enabled", - "global_only": "True" + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false } } } diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index 3ed8c3f42c..d70d55ecf4 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -5,10 +5,7 @@ "inherits": "fdmprinter", "metadata": { "author": "Ultimaker", - "manufacturer": "Ultimaker", + "manufacturer": "Ultimaker" "visible": false, - "preferred_profile": "Normal Quality", - "preferred_nozzle": "0.4 mm", - "preferred_material": "PLA" } } diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json index 9f68c2a16b..b29f904095 100644 --- a/resources/definitions/ultimaker2_plus.def.json +++ b/resources/definitions/ultimaker2_plus.def.json @@ -12,8 +12,12 @@ "platform": "ultimaker2_platform.obj", "platform_texture": "Ultimaker2Plusbackplate.png", "preferred_variant": "ultimaker2_plus_0.4", - "preferred_material": "pla", - "preferred_quality": "high" + "preferred_material": "*pla*", + "preferred_quality": "*normal*", + "has_variants": true, + "has_materials": true, + "has_machine_materials": true, + "has_machine_quality": true }, "overrides": { diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index 1dbdb95d8c..55668946a0 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -11,6 +11,9 @@ "file_formats": "text/x-gcode", "icon": "icon_ultimaker.png", "platform": "ultimaker_platform.stl", + "has_materials": true, + "preferred_material": "*pla*", + "preferred_quality": "*normal*", "pages": [ "SelectUpgradedParts", "UpgradeFirmware", diff --git a/resources/instances/high_quality.inst.cfg b/resources/instances/high_quality.inst.cfg deleted file mode 100644 index 2e860cf380..0000000000 --- a/resources/instances/high_quality.inst.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[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 deleted file mode 100644 index 6bb23d841c..0000000000 --- a/resources/instances/normal_quality.inst.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[general] -version = 2 -name = normal -definition = fdmprinter - -[metadata] -type = quality - -[values] diff --git a/resources/materials/abs.inst.cfg b/resources/materials/abs.inst.cfg deleted file mode 100644 index 0d64e81437..0000000000 --- a/resources/materials/abs.inst.cfg +++ /dev/null @@ -1,12 +0,0 @@ -[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 deleted file mode 100644 index ca30cba046..0000000000 --- a/resources/materials/cpe.inst.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[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_abs.xml.fdm_material b/resources/materials/generic_abs.xml.fdm_material new file mode 100644 index 0000000000..654b06d221 --- /dev/null +++ b/resources/materials/generic_abs.xml.fdm_material @@ -0,0 +1,34 @@ + + + + + + Generic + ABS + Generic + + 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 0 + #FF0000 + + + 1.07 + 2.85 + + + 250 + 80 + + + + + + + + + + + + diff --git a/resources/materials/generic_cpe.xml.fdm_material b/resources/materials/generic_cpe.xml.fdm_material new file mode 100644 index 0000000000..bbe6e328d2 --- /dev/null +++ b/resources/materials/generic_cpe.xml.fdm_material @@ -0,0 +1,34 @@ + + + + + + Generic + CPE + Generic + + 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 0 + #0000FF + + + 0.94 + 2.85 + + + 250 + 70 + + + + + + + + + + + + diff --git a/resources/materials/generic_pla.xml.fdm_material b/resources/materials/generic_pla.xml.fdm_material index a4fe02c195..40432d5849 100644 --- a/resources/materials/generic_pla.xml.fdm_material +++ b/resources/materials/generic_pla.xml.fdm_material @@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 0 - #FFFFFF + #00FF00 1.3 @@ -20,28 +20,15 @@ Generic PLA profile. Serves as an example file, data in this file is not correct 210 60 - 175 - 150 - - - - - - - - - 150 - - 80 - - - 100 - + + + + diff --git a/resources/materials/pla.inst.cfg b/resources/materials/pla.inst.cfg deleted file mode 100644 index dfa9c62469..0000000000 --- a/resources/materials/pla.inst.cfg +++ /dev/null @@ -1,10 +0,0 @@ -[general] -version = 2 -name = PLA -definition = fdmprinter - -[metadata] -type = material - -[values] -material_bed_temperature = 60 diff --git a/resources/profiles/general/High+Quality.cfg b/resources/profiles/general/High+Quality.cfg deleted file mode 100644 index a006c7a995..0000000000 --- a/resources/profiles/general/High+Quality.cfg +++ /dev/null @@ -1,10 +0,0 @@ -[general] -version = 1 -name = High Quality -weight = -3 - -[settings] -layer_height = 0.06 -speed_topbottom = 15 -speed_infill = 80 - diff --git a/resources/profiles/general/Normal+Quality.cfg b/resources/profiles/general/Normal+Quality.cfg deleted file mode 100644 index 575c3343e2..0000000000 --- a/resources/profiles/general/Normal+Quality.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[general] -version = 1 -name = Normal Quality -weight = -2 - -[settings] -speed_topbottom = 15 -speed_infill = 80 \ No newline at end of file diff --git a/resources/profiles/general/Ulti+Quality.cfg b/resources/profiles/general/Ulti+Quality.cfg deleted file mode 100644 index 1fa9b6085c..0000000000 --- a/resources/profiles/general/Ulti+Quality.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[general] -version = 1 -name = Ulti Quality -weight = -4 - -[settings] -layer_height = 0.04 -speed_topbottom = 15 -speed_infill = 80 \ No newline at end of file diff --git a/resources/profiles/materials/abs.cfg b/resources/profiles/materials/abs.cfg deleted file mode 100644 index 67abc32810..0000000000 --- a/resources/profiles/materials/abs.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[general] -version = 1 -type = material -name = ABS - -[settings] -material_print_temperature = 250 -material_bed_temperature = 80 -material_flow = 107 diff --git a/resources/profiles/materials/cpe.cfg b/resources/profiles/materials/cpe.cfg deleted file mode 100644 index 0621260745..0000000000 --- a/resources/profiles/materials/cpe.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[general] -version = 1 -type = material -name = CPE - -[settings] -material_print_temperature = 250 -material_bed_temperature = 70 \ No newline at end of file diff --git a/resources/profiles/materials/pla.cfg b/resources/profiles/materials/pla.cfg deleted file mode 100644 index b5af61b9b6..0000000000 --- a/resources/profiles/materials/pla.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[general] -version = 1 -type = material -name = PLA - -[settings] -material_bed_temperature = 60 \ No newline at end of file diff --git a/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile b/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile deleted file mode 100644 index db091d8e8d..0000000000 --- a/resources/profiles/ultimaker2+/pla_0.4_ulti.curaprofile +++ /dev/null @@ -1,21 +0,0 @@ -[general] -version = 1 -name = Ulti Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = PLA -weight = -4 - -[settings] -line_width = 0.35 -layer_height = 0.04 -layer_height_0 = 0.26 -wall_thickness = 1.4 -top_bottom_thickness = 1.12 -infill_sparse_density = 25 -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 diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index f3bce7b993..7e03bd7102 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -6,6 +6,7 @@ pragma Singleton import QtQuick 2.2 import QtQuick.Controls 1.1 import UM 1.1 as UM +import Cura 1.0 as Cura Item { @@ -109,24 +110,24 @@ Item 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(); + enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings"); + onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer() } Action { id: resetProfileAction; - enabled: UM.ActiveProfile.valid && UM.ActiveProfile.hasCustomisedValues - text: catalog.i18nc("@action:inmenu menubar:profile","&Reload Current Profile"); - onTriggered: UM.ActiveProfile.discardChanges(); + enabled: Cura.MachineManager.hasUserSettings + text: catalog.i18nc("@action:inmenu menubar:profile","&Discard current settings"); + onTriggered: Cura.MachineManager.clearUserSettings(); } Action { id: addProfileAction; - enabled: UM.ActiveProfile.valid - text: catalog.i18nc("@action:inmenu menubar:profile","&Create New Profile..."); + enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings + text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings..."); } Action diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a7f4a43c22..c3dc95b454 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -10,8 +10,6 @@ import QtQuick.Dialogs 1.1 import UM 1.2 as UM import Cura 1.0 as Cura -import "." - UM.MainWindow { id: base @@ -56,7 +54,7 @@ UM.MainWindow title: catalog.i18nc("@title:menu menubar:toplevel","&File"); MenuItem { - action: Actions.open; + action: Cura.Actions.open; } Menu @@ -95,7 +93,7 @@ UM.MainWindow text: catalog.i18nc("@action:inmenu menubar:file", "&Save Selection to File"); enabled: UM.Selection.hasSelection; iconName: "document-save-as"; - onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", Printer.jobName, { "filter_by_machine": false }); + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false }); } Menu { @@ -111,18 +109,18 @@ UM.MainWindow MenuItem { text: model.description; - onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, Printer.jobName, { "filter_by_machine": false }); + onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, PrintInformation.jobName, { "filter_by_machine": false }); } onObjectAdded: saveAllMenu.insertItem(index, object) onObjectRemoved: saveAllMenu.removeItem(object) } } - MenuItem { action: Actions.reloadAll; } + MenuItem { action: Cura.Actions.reloadAll; } MenuSeparator { } - MenuItem { action: Actions.quit; } + MenuItem { action: Cura.Actions.quit; } } Menu @@ -130,17 +128,17 @@ UM.MainWindow //: Edit menu title: catalog.i18nc("@title:menu menubar:toplevel","&Edit"); - MenuItem { action: Actions.undo; } - MenuItem { action: Actions.redo; } + MenuItem { action: Cura.Actions.undo; } + MenuItem { action: Cura.Actions.redo; } MenuSeparator { } - MenuItem { action: Actions.deleteSelection; } - MenuItem { action: Actions.deleteAll; } - MenuItem { action: Actions.resetAllTranslation; } - MenuItem { action: Actions.resetAll; } + MenuItem { action: Cura.Actions.deleteSelection; } + MenuItem { action: Cura.Actions.deleteAll; } + MenuItem { action: Cura.Actions.resetAllTranslation; } + MenuItem { action: Cura.Actions.resetAll; } MenuSeparator { } - MenuItem { action: Actions.groupObjects;} - MenuItem { action: Actions.mergeObjects;} - MenuItem { action: Actions.unGroupObjects;} + MenuItem { action: Cura.Actions.groupObjects;} + MenuItem { action: Cura.Actions.mergeObjects;} + MenuItem { action: Cura.Actions.unGroupObjects;} } Menu @@ -180,7 +178,7 @@ UM.MainWindow text: model.name; checkable: true; checked: Cura.MachineManager.activeMachineId == model.id - exclusiveGroup: machineSelectionMenuGroup; + exclusiveGroup: machineMenuGroup; onTriggered: Cura.MachineManager.setActiveMachine(model.id); } onObjectAdded: machineMenu.insertItem(index, object) @@ -216,8 +214,8 @@ UM.MainWindow MenuSeparator { visible: Cura.MachineManager.hasVariants; } - MenuItem { action: Actions.addMachine; } - MenuItem { action: Actions.configureMachines; } + MenuItem { action: Cura.Actions.addMachine; } + MenuItem { action: Cura.Actions.configureMachines; } } Menu @@ -228,7 +226,26 @@ UM.MainWindow Instantiator { id: profileMenuInstantiator -// model: UM.ProfilesModel {} + model: UM.InstanceContainersModel + { + filter: + { + var result = { "type": "quality" }; + if(Cura.MachineManager.filterQualityByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId; + if(Cura.MachineManager.hasMaterials) + { + result.material = Cura.MachineManager.activeMaterialId; + } + } + else + { + result.definition = "fdmprinter" + } + return result + } + } property int separatorIndex: -1 Loader { @@ -271,30 +288,20 @@ UM.MainWindow { id: item text: model_data ? model_data.name : "" - checkable: true; - checked: model_data ? model_data.active : false; - exclusiveGroup: profileMenuGroup; - onTriggered: - { - UM.MachineManager.setActiveProfile(model_data.name); - if (!model_data.active) { - //Selecting a profile was canceled; undo menu selection - profileMenuInstantiator.model.setProperty(model_index, "active", false); - var activeProfileName = UM.MachineManager.activeProfile; - var activeProfileIndex = profileMenuInstantiator.model.find("name", activeProfileName); - profileMenuInstantiator.model.setProperty(activeProfileIndex, "active", true); - } - } + checkable: true + checked: Cura.MachineManager.activeQualityId == model_data.id + exclusiveGroup: profileMenuGroup + onTriggered: Cura.MachineManager.setActiveQuality(model_data.id) } } MenuSeparator { id: profileMenuSeparator } - MenuItem { action: Actions.updateProfile; } - MenuItem { action: Actions.resetProfile; } - MenuItem { action: Actions.addProfile; } + MenuItem { action: Cura.Actions.addProfile } + MenuItem { action: Cura.Actions.updateProfile } + MenuItem { action: Cura.Actions.resetProfile } MenuSeparator { } - MenuItem { action: Actions.manageProfiles; } + MenuItem { action: Cura.Actions.manageProfiles } } Menu @@ -305,7 +312,7 @@ UM.MainWindow Instantiator { - id: extenions + id: extensions model: UM.ExtensionModel { } Menu @@ -320,7 +327,7 @@ UM.MainWindow MenuItem { text: model.text - onTriggered: extenions.model.subMenuTriggered(name, model.text) + onTriggered: extensions.model.subMenuTriggered(name, model.text) } onObjectAdded: sub_menu.insertItem(index, object) onObjectRemoved: sub_menu.removeItem(object) @@ -337,7 +344,7 @@ UM.MainWindow //: Settings menu title: catalog.i18nc("@title:menu menubar:toplevel","&Settings"); - MenuItem { action: Actions.preferences; } + MenuItem { action: Cura.Actions.preferences; } } Menu @@ -345,11 +352,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: Cura.Actions.showEngineLog; } + MenuItem { action: Cura.Actions.documentation; } + MenuItem { action: Cura.Actions.reportBug; } MenuSeparator { } - MenuItem { action: Actions.about; } + MenuItem { action: Cura.Actions.about; } } } @@ -439,7 +446,7 @@ UM.MainWindow left: parent.left; //leftMargin: UM.Theme.getSize("loadfile_margin").width } - action: Actions.open; + action: Cura.Actions.open; } Image @@ -526,13 +533,6 @@ UM.MainWindow } width: UM.Theme.getSize("sidebar").width; - - addMachineAction: Actions.addMachine; - configureMachinesAction: Actions.configureMachines; - addProfileAction: Actions.addProfile; - updateProfileAction: Actions.updateProfile; - resetProfileAction: Actions.resetProfile; - manageProfilesAction: Actions.manageProfiles; } } } @@ -573,16 +573,16 @@ UM.MainWindow Connections { - target: Actions.preferences + target: Cura.Actions.preferences onTriggered: preferences.visible = true } Connections { - target: Actions.addProfile + target: Cura.Actions.addProfile onTriggered: { - UM.MachineManager.createProfile(); + Cura.MachineManager.newQualityContainerFromQualityAndUser(); preferences.setPage(5); preferences.show(); @@ -593,7 +593,7 @@ UM.MainWindow Connections { - target: Actions.configureMachines + target: Cura.Actions.configureMachines onTriggered: { preferences.visible = true; @@ -603,7 +603,7 @@ UM.MainWindow Connections { - target: Actions.manageProfiles + target: Cura.Actions.manageProfiles onTriggered: { preferences.visible = true; @@ -613,7 +613,7 @@ UM.MainWindow Connections { - target: Actions.configureSettingVisibility + target: Cura.Actions.configureSettingVisibility onTriggered: { preferences.visible = true; @@ -636,22 +636,22 @@ UM.MainWindow id: objectContextMenu; property variant objectId: -1; - MenuItem { action: Actions.centerObject; } - MenuItem { action: Actions.deleteObject; } - MenuItem { action: Actions.multiplyObject; } + MenuItem { action: Cura.Actions.centerObject; } + MenuItem { action: Cura.Actions.deleteObject; } + MenuItem { action: Cura.Actions.multiplyObject; } MenuSeparator { } - MenuItem { action: Actions.deleteAll; } - MenuItem { action: Actions.reloadAll; } - MenuItem { action: Actions.resetAllTranslation; } - MenuItem { action: Actions.resetAll; } + MenuItem { action: Cura.Actions.deleteAll; } + MenuItem { action: Cura.Actions.reloadAll; } + MenuItem { action: Cura.Actions.resetAllTranslation; } + MenuItem { action: Cura.Actions.resetAll; } MenuSeparator { } - MenuItem { action: Actions.groupObjects; } - MenuItem { action: Actions.mergeObjects; } - MenuItem { action: Actions.unGroupObjects; } + MenuItem { action: Cura.Actions.groupObjects; } + MenuItem { action: Cura.Actions.mergeObjects; } + MenuItem { action: Cura.Actions.unGroupObjects; } Connections { - target: Actions.deleteObject + target: Cura.Actions.deleteObject onTriggered: { if(objectContextMenu.objectId != 0) @@ -664,7 +664,7 @@ UM.MainWindow Connections { - target: Actions.multiplyObject + target: Cura.Actions.multiplyObject onTriggered: { if(objectContextMenu.objectId != 0) @@ -677,7 +677,7 @@ UM.MainWindow Connections { - target: Actions.centerObject + target: Cura.Actions.centerObject onTriggered: { if(objectContextMenu.objectId != 0) @@ -692,14 +692,14 @@ UM.MainWindow Menu { id: contextMenu; - MenuItem { action: Actions.deleteAll; } - MenuItem { action: Actions.reloadAll; } - MenuItem { action: Actions.resetAllTranslation; } - MenuItem { action: Actions.resetAll; } + MenuItem { action: Cura.Actions.deleteAll; } + MenuItem { action: Cura.Actions.reloadAll; } + MenuItem { action: Cura.Actions.resetAllTranslation; } + MenuItem { action: Cura.Actions.resetAll; } MenuSeparator { } - MenuItem { action: Actions.groupObjects; } - MenuItem { action: Actions.mergeObjects; } - MenuItem { action: Actions.unGroupObjects; } + MenuItem { action: Cura.Actions.groupObjects; } + MenuItem { action: Cura.Actions.mergeObjects; } + MenuItem { action: Cura.Actions.unGroupObjects; } } Connections @@ -720,13 +720,13 @@ UM.MainWindow Connections { - target: Actions.quit + target: Cura.Actions.quit onTriggered: base.visible = false; } Connections { - target: Actions.toggleFullScreen + target: Cura.Actions.toggleFullScreen onTriggered: base.toggleFullscreen(); } @@ -756,7 +756,7 @@ UM.MainWindow Connections { - target: Actions.open + target: Cura.Actions.open onTriggered: openDialog.open() } @@ -767,7 +767,7 @@ UM.MainWindow Connections { - target: Actions.showEngineLog + target: Cura.Actions.showEngineLog onTriggered: engineLog.visible = true; } @@ -778,7 +778,7 @@ UM.MainWindow Connections { - target: Actions.addMachine + target: Cura.Actions.addMachine onTriggered: addMachineDialog.visible = true; } @@ -789,7 +789,7 @@ UM.MainWindow Connections { - target: Actions.about + target: Cura.Actions.about onTriggered: aboutDialog.visible = true; } diff --git a/resources/qml/GeneralPage.qml b/resources/qml/GeneralPage.qml index dcd5c66d73..0ae783916b 100644 --- a/resources/qml/GeneralPage.qml +++ b/resources/qml/GeneralPage.qml @@ -5,7 +5,6 @@ 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 @@ -207,10 +206,11 @@ UM.PreferencesPage } } - DelegateModel + ListView { id: plugins model: UM.PluginsModel { } + visible: false } } } diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 9551e3bcf5..e73bf145de 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -10,62 +10,25 @@ import UM 1.1 as UM import Cura 1.0 as Cura Rectangle { - id: base; + id: base - property bool activity: Printer.getPlatformActivity; + property bool activity: Printer.getPlatformActivity property string fileBaseName property variant activeMachineName: Cura.MachineManager.activeMachineName onActiveMachineNameChanged: { - base.createFileName() + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } UM.I18nCatalog { id: catalog; name:"cura"} - property variant printDuration: PrintInformation.currentPrintTime; - property real printMaterialAmount: PrintInformation.materialAmount; + property variant printDuration: PrintInformation.currentPrintTime + property real printMaterialAmount: PrintInformation.materialAmount height: childrenRect.height color: "transparent" - 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 - { - 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]; - } - } - printJobTextfield.text = abbrMachine + "_" + base.fileBaseName; - } - else - { - printJobTextfield.text = base.fileBaseName; - } - } - Connections { target: backgroundItem @@ -78,20 +41,20 @@ Rectangle { onActivityChanged: { if (activity == true && base.fileBaseName == ''){ //this only runs when you open a file from the terminal (or something that works the same way; for example when you drag a file on the icon in MacOS or use 'open with' on Windows) - base.fileBaseName = Printer.jobName //it gets the fileBaseName from CuraApplication.py because this saves the filebase when the file is opened using the terminal (or something alike) - base.createFileName() + base.fileBaseName = PrintInformation.jobName; //get the fileBaseName from PrintInformation.py because this saves the filebase when the file is opened using the terminal (or something alike) + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } if (activity == true && base.fileBaseName != ''){ //this runs in all other cases where there is a mesh on the buildplate (activity == true). It uses the fileBaseName from the hasMesh signal - base.createFileName() + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } if (activity == false){ //When there is no mesh in the buildplate; the printJobTextField is set to an empty string so it doesn't set an empty string as a jobName (which is later used for saving the file) - printJobTextfield.text = '' + printJobTextfield.text = ''; } } - Rectangle + Rectangle { id: jobNameRow anchors.top: parent.top @@ -112,22 +75,22 @@ Rectangle { width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height - onClicked: + onClicked: { - printJobTextfield.selectAll() - printJobTextfield.focus = true + printJobTextfield.selectAll(); + printJobTextfield.focus = true; } style: ButtonStyle { background: Rectangle { color: "transparent" - UM.RecolorImage + UM.RecolorImage { - width: UM.Theme.getSize("save_button_specs_icons").width - height: UM.Theme.getSize("save_button_specs_icons").height - sourceSize.width: width - sourceSize.height: width + width: UM.Theme.getSize("save_button_specs_icons").width; + height: UM.Theme.getSize("save_button_specs_icons").height; + sourceSize.width: width; + sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("text"); source: UM.Theme.getIcon("pencil"); } @@ -147,15 +110,15 @@ Rectangle { text: '' horizontalAlignment: TextInput.AlignRight onTextChanged: { - Printer.setJobName(text) + PrintInformation.setJobName(text); } onEditingFinished: { if (printJobTextfield.text != ''){ - printJobTextfield.focus = false + printJobTextfield.focus = false; } } validator: RegExpValidator { - regExp: /^[^\\ \/ \.]*$/ + regExp: /^[^\\ \/ \*\?\|\[\]]*$/ } style: TextFieldStyle{ textColor: UM.Theme.getColor("setting_control_text"); @@ -200,7 +163,7 @@ Rectangle { sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("text_subtext") - source: UM.Theme.getIcon("print_time"); + source: UM.Theme.getIcon("print_time") } Label{ id: timeSpec @@ -221,7 +184,7 @@ Rectangle { sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("text_subtext") - source: UM.Theme.getIcon("category_material"); + source: UM.Theme.getIcon("category_material") } Label{ id: lengthSpec @@ -229,7 +192,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_subtext") - text: base.printMaterialAmount <= 0 ? catalog.i18nc("@label", "0.0 m") : catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount) + text: catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount > 0 ? base.printMaterialAmount : 0) } } } diff --git a/resources/qml/MachinesPage.qml b/resources/qml/MachinesPage.qml index 8c7a2c521d..faef019deb 100644 --- a/resources/qml/MachinesPage.qml +++ b/resources/qml/MachinesPage.qml @@ -12,11 +12,19 @@ UM.ManagementPage id: base; title: catalog.i18nc("@title:tab", "Printers"); - property int numInstances: model.rowCount(); model: UM.ContainerStacksModel { filter: {"type": "machine"} - onDataChanged: numInstances = model.rowCount() + } + + activeId: Cura.MachineManager.activeMachineId + activeIndex: { + for(var i = 0; i < model.rowCount(); i++) { + if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { + return i; + } + } + return -1; } onAddObject: Printer.requestAddPrinter() @@ -24,9 +32,9 @@ UM.ManagementPage onRenameObject: renameDialog.open(); onActivateObject: Cura.MachineManager.setActiveMachine(base.currentItem.id) - removeEnabled: base.currentItem != null && numInstances > 1 - renameEnabled: base.currentItem != null && numInstances > 0 - activateEnabled: base.currentItem != null + removeEnabled: base.currentItem != null && model.rowCount() > 1 + renameEnabled: base.currentItem != null + activateEnabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMachineId Flow { diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 03ede39a5c..af0f0c1bd2 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -14,13 +14,37 @@ UM.ManagementPage 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 + model: UM.InstanceContainersModel + { + filter: + { + var result = { "type": "material" } + if(Cura.MachineManager.filterMaterialsByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId + if(Cura.MachineManager.hasVariants) + { + result.variant = Cura.MachineManager.activeVariantId + } + } + else + { + result.definition = "fdmprinter" + } + return result + } + } + + activeId: Cura.MachineManager.activeMaterialId + activeIndex: { + for(var i = 0; i < model.rowCount(); i++) { + if (model.getItem(i).id == Cura.MachineManager.activeMaterialId) { + return i; + } + } + return -1; + } + addEnabled: false removeEnabled: false renameEnabled: false @@ -185,7 +209,7 @@ UM.ManagementPage onCurrentItemChanged: { - if(!currentItem == null) + if(currentItem == null) { return } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 6e5d39cb49..670bf79956 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -6,25 +6,70 @@ 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") + addText: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate") - model: UM.InstanceContainersModel { filter: { "type": "quality" } } - - onAddObject: { - var selectedProfile; - if (objectList.currentIndex == 0) { - // Current settings - selectedProfile = UM.MachineManager.createProfile(); - } else { - selectedProfile = UM.MachineManager.duplicateProfile(currentItem.name); + model: UM.InstanceContainersModel + { + filter: + { + var result = { "type": "quality" }; + if(Cura.MachineManager.filterQualityByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId; + if(Cura.MachineManager.hasMaterials) + { + result.material = Cura.MachineManager.activeMaterialId; + } + } + else + { + result.definition = "fdmprinter" + } + return result } - base.selectProfile(selectedProfile); + } + + section.property: "readOnly" + section.delegate: Rectangle + { + width: objectListContainer.viewport.width; + height: childrenRect.height; + + Label + { + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_lining").width; + text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles") + font.bold: true + } + } + + activeId: Cura.MachineManager.activeQualityId + activeIndex: { + for(var i = 0; i < model.rowCount(); i++) { + if (model.getItem(i).id == Cura.MachineManager.activeQualityId) { + return i; + } + } + return -1; + } + + onActivateObject: Cura.MachineManager.setActiveQuality(currentItem.id) + onAddObject: { + var selectedContainer; + if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) { + selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser(); + } else { + selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id); + } + base.selectContainer(selectedContainer); renameDialog.removeWhenRejected = true; renameDialog.open(); @@ -33,15 +78,19 @@ UM.ManagementPage 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) + scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName) - signal selectProfile(string name) - onSelectProfile: { - objectList.currentIndex = objectList.model.find("name", name); + signal showProfileNameDialog() + onShowProfileNameDialog: { renameDialog.removeWhenRejected = true; renameDialog.open(); renameDialog.selectText(); } + + signal selectContainer(string id) + onSelectContainer: { + objectList.currentIndex = objectList.model.find("id", id); } Item { @@ -56,71 +105,109 @@ UM.ManagementPage elide: Text.ElideRight } - ScrollView { + Row { + id: currentSettingsActions + visible: currentItem.id == Cura.MachineManager.activeQualityId + anchors.left: parent.left anchors.top: profileName.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height + + Button + { + text: { + return catalog.i18nc("@action:button", "Update profile with current settings"); + } + enabled: Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + onClicked: Cura.MachineManager.updateQualityContainerFromUserContainer() + } + + Button + { + text: catalog.i18nc("@action:button", "Discard current settings"); + enabled: Cura.MachineManager.hasUserSettings + onClicked: Cura.MachineManager.clearUserSettings(); + } + } + + Column { + id: profileNotices + anchors.top: currentSettingsActions.visible ? currentSettingsActions.bottom : currentSettingsActions.anchors.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.left: parent.left + anchors.right: parent.right + spacing: UM.Theme.getSize("default_margin").height + + Label { + id: defaultsMessage + visible: !currentItem.metadata.has_settings + text: catalog.i18nc("@action:label", "This profile has no settings and uses the defaults specified by the printer.") + wrapMode: Text.WordWrap + width: parent.width + } + Label { + id: noCurrentSettingsMessage + visible: currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings + text: catalog.i18nc("@action:label", "Your current settings match the selected profile.") + wrapMode: Text.WordWrap + width: parent.width + } + } + + ScrollView { + id: scrollView + + anchors.left: parent.left + anchors.top: profileNotices.visible ? profileNotices.bottom : profileNotices.anchors.top + 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 || base.currentItem.active - 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() + ListView { + model: Cura.ContainerSettingsModel{ containers: (currentItem.id == Cura.MachineManager.activeQualityId) ? [base.currentItem.id, Cura.MachineManager.activeUserProfileId] : [base.currentItem.id] } + delegate: Row { + property variant setting: model + spacing: UM.Theme.getSize("default_margin").width/2 + Label { + text: model.label + elide: Text.ElideMiddle + width: scrollView.width / 100 * 40 } - - Button - { - text: catalog.i18nc("@action:button", "Discard changes"); - enabled: UM.ActiveProfile.hasCustomisedValues - onClicked: UM.ActiveProfile.discardChanges() + Repeater { + model: setting.values.length + Label { + text: setting.values[index].toString() + width: scrollView.width / 100 * 15 + elide: Text.ElideRight + font.strikeout: index < setting.values.length - 1 && setting.values[index + 1] != "" + opacity: font.strikeout ? 0.5 : 1 + } + } + Label { + text: model.unit } } - - Grid - { - id: containerGrid - columns: 2 + header: Row { + visible: currentItem.id == Cura.MachineManager.activeQualityId 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") + text: catalog.i18nc("@action:label", "Profile:") + width: scrollView.width / 100 * 55 + horizontalAlignment: Text.AlignRight + font.bold: true } 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(); } - } + text: catalog.i18nc("@action:label", "Current:") + visible: currentItem.id == Cura.MachineManager.activeQualityId + font.bold: true } } + section.property: "category" + section.criteria: ViewSection.FullString + section.delegate: Label { + text: section + font.bold: true + } } } } @@ -136,9 +223,10 @@ UM.ManagementPage Button { - text: catalog.i18nc("@action:button", "Export"); - iconName: "document-export"; - onClicked: exportDialog.open(); + text: catalog.i18nc("@action:button", "Export") + iconName: "document-export" + onClicked: exportDialog.open() + enabled: currentItem != null } } @@ -148,19 +236,19 @@ UM.ManagementPage UM.ConfirmRemoveDialog { - id: confirmDialog; - object: base.currentItem != null ? base.currentItem.name : ""; - onYes: base.model.removeProfile(base.currentItem.name); + id: confirmDialog + object: base.currentItem != null ? base.currentItem.name : "" + onYes: Cura.MachineManager.removeQualityContainer(base.currentItem.id) } UM.RenameDialog { id: renameDialog; - object: base.currentItem != null ? base.currentItem.name : ""; - property bool removeWhenRejected: false; - onAccepted: base.model.renameProfile(base.currentItem.name, newName.trim()); + object: base.currentItem != null ? base.currentItem.name : "" + property bool removeWhenRejected: false + onAccepted: Cura.MachineManager.renameQualityContainer(base.currentItem.id, newName) onRejected: { if(removeWhenRejected) { - base.model.removeProfile(base.currentItem.name) + Cura.MachineManager.removeQualityContainer(base.currentItem.id) } } } @@ -208,7 +296,7 @@ UM.ManagementPage folder: base.model.getDefaultPath() onAccepted: { - var result = base.model.exportProfile(base.currentItem.id, base.currentItem.name, fileUrl, selectedNameFilter) + var result = base.model.exportProfile(base.currentItem.id, fileUrl, selectedNameFilter) if(result && result.status == "error") { messageDialog.icon = StandardIcon.Critical diff --git a/resources/qml/ProfileSetup.qml b/resources/qml/ProfileSetup.qml deleted file mode 100644 index 95aed3685c..0000000000 --- a/resources/qml/ProfileSetup.qml +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2015 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.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Item{ - id: base; - UM.I18nCatalog { id: catalog; name:"cura"} - 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() - - Rectangle{ - id: globalProfileRow - anchors.top: base.top - height: UM.Theme.getSize("sidebar_setup").height - width: base.width - - Label{ - id: globalProfileLabel - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width; - anchors.verticalCenter: parent.verticalCenter - text: catalog.i18nc("@label","Profile:"); - width: parent.width/100*45 - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton { - property int rightMargin: customisedSettings.visible ? customisedSettings.width + UM.Theme.getSize("default_margin").width / 2 : 0 - - id: globalProfileSelection - 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: Cura.MachineManager.activeQualityName - style: UM.Theme.styles.sidebar_header_button - - menu: Menu - { - id: profileSelectionMenu - Instantiator - { - id: profileSelectionInstantiator - model: UM.InstanceContainersModel - { - filter: {"type": "quality"} - } - property int separatorIndex: -1 - - Loader { - property QtObject model_data: model - property int model_index: index - sourceComponent: menuItemDelegate - } - onObjectAdded: - { - //Insert a separator between readonly and custom profiles - if(separatorIndex < 0 && index > 0) { - if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) { - profileSelectionMenu.insertSeparator(index); - separatorIndex = index; - } - } - //Because of the separator, custom profiles move one index lower - profileSelectionMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item); - } - onObjectRemoved: - { - //When adding a profile, the menu is rebuild by removing all items. - //If a separator was added, we need to remove that too. - if(separatorIndex >= 0) - { - profileSelectionMenu.removeItem(profileSelectionMenu.items[separatorIndex]) - separatorIndex = -1; - } - profileSelectionMenu.removeItem(object.item); - } - } - ExclusiveGroup { id: profileSelectionMenuGroup; } - - Component - { - id: menuItemDelegate - MenuItem - { - id: item - text: model_data ? model_data.name : "" - checkable: true; - checked: Cura.MachineManager.activeQualityId == model_data.id - exclusiveGroup: profileSelectionMenuGroup; - onTriggered: - { - 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; - } - } - } - UM.SimpleButton { - id: customisedSettings - - visible: UM.ActiveProfile.hasCustomisedValues - height: parent.height * 0.6 - width: parent.height * 0.6 - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("setting_preferences_button_margin").width - - color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("star"); - - onClicked: base.manageProfilesAction.trigger() - onEntered: - { - var content = catalog.i18nc("@tooltip","Some setting values are different from the values stored in the profile.\n\nClick to open the profile manager.") - base.showTooltip(globalProfileRow, Qt.point(0, globalProfileRow.height / 2), content) - } - onExited: base.hideTooltip() - } - } -} diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 1307e8f820..8b95de15ee 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -98,7 +98,7 @@ Rectangle { text: UM.OutputDeviceManager.activeDeviceShortDescription onClicked: { - UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, Printer.jobName, { "filter_by_machine": true }) + UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, { "filter_by_machine": true }) } style: ButtonStyle { diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index f4e3dbe5ae..6e23158a1c 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -7,8 +7,7 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 import UM 1.1 as UM - -import ".." +import Cura 1.0 as Cura Button { id: base; @@ -45,7 +44,7 @@ Button { iconSource: UM.Theme.getIcon("settings"); onClicked: { - Actions.configureSettingVisibility.trigger(definition) + Cura.Actions.configureSettingVisibility.trigger(definition) } } @@ -57,9 +56,9 @@ Button { anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("setting_preferences_button_margin").width - visible: hiddenValuesCount > 0 - height: parent.height / 2; - width: height; + visible: false //hiddenValuesCount > 0 + height: parent.height / 2 + width: height onClicked: { base.showAllHiddenInheritedSettings() diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index 6f0314160e..1d66e509d0 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -16,6 +16,7 @@ SettingItem { id: control anchors.fill: parent + hoverEnabled: true property bool checked: { diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml new file mode 100644 index 0000000000..0160dab7fa --- /dev/null +++ b/resources/qml/Settings/SettingExtruder.qml @@ -0,0 +1,114 @@ +// 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.Controls.Styles 1.1 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +SettingItem +{ + id: base + + contents: ComboBox + { + id: control + + model: Cura.ExtrudersModel + { + id: extruders_model + } + textRole: "name" + + 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 extruders_model.getItem(index).colour; + } + } + 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") : extruders_model.getItem(index).colour + + 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", extruders_model.getItem(index).index); + onModelChanged: updateCurrentIndex(); + + Connections + { + target: provider + onPropertiesChanged: control.updateCurrentIndex(); + } + + function updateCurrentIndex() + { + for(var i = 0; i < extruders_model.rowCount(); ++i) + { + if(extruders_model.getItem(i).index == provider.properties.value) + { + currentIndex = i; + return; + } + } + currentIndex = -1; + } + } +} diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 013dcd1680..4fd84759fd 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -18,10 +18,51 @@ Item { property alias contents: controlContainer.children; property alias hovered: mouse.containsMouse + property var showRevertButton: true + property var showInheritButton: true + property var doDepthIndentation: true + + // Create properties to put property provider stuff in (bindings break in qt 5.5.1 otherwise) + property var state: propertyProvider.properties.state + property var stackLevels: propertyProvider.stackLevels + property var stackLevel: stackLevels[0] + signal contextMenuRequested() signal showTooltip(string text); signal hideTooltip(); + property string tooltipText: + { + 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) + } + + return tooltip + } + MouseArea { id: mouse; @@ -50,7 +91,10 @@ Item { interval: 500; repeat: false; - onTriggered: base.showTooltip(definition.description); + onTriggered: + { + base.showTooltip(base.tooltipText); + } } Label @@ -58,7 +102,7 @@ Item { 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.leftMargin: doDepthIndentation ? (UM.Theme.getSize("section_icon_column").width + 5) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width) : 0 anchors.right: settingControls.left; anchors.verticalCenter: parent.verticalCenter @@ -89,7 +133,7 @@ Item { { id: revertButton; - visible: propertyProvider.stackLevel == 0 + visible: base.stackLevel == 0 && base.showRevertButton height: parent.height; width: height; @@ -106,24 +150,72 @@ Item { 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); + onEntered: { hoverTimer.stop(); 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(base.tooltipText); } 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 + // Inherit button needs to be visible if; + // - User made changes that override any loaded settings + // - This setting item uses inherit button at all + // - The type of the value of any deeper container is an "object" (eg; is a function) + visible: + { + var state = base.state == "InstanceState.User"; + var has_setting_function = false; + for (var i = 1; i < base.stackLevels.length; i++) + { + has_setting_function = typeof(propertyProvider.getPropertyValue("value", base.stackLevels[i])) == "object"; + if(has_setting_function) + { + break; + } + } + return state && base.showInheritButton && has_setting_function && typeof(propertyProvider.getPropertyValue("value", base.stackLevels[0])) != "object" + } height: parent.height; width: height; onClicked: { focus = true; - propertyProvider.removeFromContainer(propertyProvider.stackLevel) + + // Get the most shallow function value (eg not a number) that we can find. + var last_entry = propertyProvider.stackLevels[propertyProvider.stackLevels.length] + for (var i = 1; i < base.stackLevels.length; i++) + { + var has_setting_function = typeof(propertyProvider.getPropertyValue("value", base.stackLevels[i])) == "object"; + if(has_setting_function) + { + last_entry = propertyProvider.stackLevels[i] + break; + } + } + + if(last_entry == 4 && base.stackLevel == 0 && base.stackLevels.length == 2) + { + // Special case of the inherit reset. If only the definition (4th container) and the first + // entry (user container) are set, we can simply remove the container. + propertyProvider.removeFromContainer(0) + } + else if(last_entry - 1 == base.stackLevel) + { + // Another special case. The setting that is overriden is only 1 instance container deeper, + // so we can remove it. + propertyProvider.removeFromContainer(0) + } + else + { + // Put that entry into the "top" instance container. + // This ensures that the value in any of the deeper containers need not be removed, which is + // needed for the reset button (which deletes the top value) to correctly go back to profile + // defaults. + propertyProvider.setPropertyValue("value", propertyProvider.getPropertyValue("value", last_entry)) + propertyProvider.setPropertyValue("state", "InstanceState.Calculated") + } } backgroundColor: UM.Theme.getColor("setting_control"); @@ -133,8 +225,8 @@ Item { 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); + onEntered: { hoverTimer.stop(); 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(base.tooltipText); } } @@ -143,6 +235,8 @@ Item { { id: controlContainer; + enabled: provider.isValueUsed + anchors.right: parent.right; anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter; diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index 9972f83aa1..bf6fc34c93 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -16,8 +16,6 @@ SettingItem 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") @@ -74,7 +72,7 @@ SettingItem { id: mouseArea anchors.fill: parent; - hoverEnabled: true; + //hoverEnabled: true; cursorShape: Qt.IBeamCursor } @@ -92,21 +90,11 @@ SettingItem 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) } @@ -123,33 +111,9 @@ SettingItem { target: input property: "text" - value: control.format(propertyProvider.properties.value) + value: 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/SettingView.qml b/resources/qml/Settings/SettingView.qml index 560e7b0803..4be2326b0a 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -9,8 +9,6 @@ import QtQuick.Layouts 1.1 import UM 1.2 as UM import Cura 1.0 as Cura -import ".." - ScrollView { id: base; @@ -26,11 +24,22 @@ ScrollView { id: contents spacing: UM.Theme.getSize("default_lining").height; + cacheBuffer: 1000000; // Set a large cache to effectively just cache every list item. model: UM.SettingDefinitionsModel { id: definitionsModel; containerId: Cura.MachineManager.activeDefinitionId - visibilityHandler: UM.SettingPreferenceVisibilityHandler {} + exclude: ["machine_settings"] + visibilityHandler: UM.SettingPreferenceVisibilityHandler { } + + filter: + { + if(ExtruderManager.activeExtruderStackId) + { + return { "settable_per_extruder": true } + } + return { } + } } delegate: Loader @@ -50,8 +59,9 @@ ScrollView //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" + //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. + asynchronous: model.type != "enum" && model.type != "extruder" + active: model.type != undefined source: { @@ -63,6 +73,8 @@ ScrollView return "SettingTextField.qml" case "enum": return "SettingComboBox.qml" + case "extruder": + return "SettingExtruder.qml" case "bool": return "SettingCheckBox.qml" case "str": @@ -78,8 +90,8 @@ ScrollView { id: provider - containerStackId: Cura.MachineManager.activeMachineId - key: model.key + containerStackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId + key: model.key ? model.key : "" watchedProperties: [ "value", "enabled", "state", "validationState" ] storeIndex: 0 } @@ -134,7 +146,7 @@ ScrollView //: Settings context menu action text: catalog.i18nc("@action:menu", "Configure setting visiblity..."); - onTriggered: Actions.configureSettingVisibility.trigger(contextMenu); + onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu); } } } diff --git a/resources/qml/Settings/SettingsConfigurationPage.qml b/resources/qml/Settings/SettingsConfigurationPage.qml deleted file mode 100644 index a2889d410a..0000000000 --- a/resources/qml/Settings/SettingsConfigurationPage.qml +++ /dev/null @@ -1,119 +0,0 @@ -// 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 4109fd1586..b7ff0743de 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -12,13 +12,6 @@ Rectangle { id: base; - property Action addMachineAction; - property Action configureMachinesAction; - property Action addProfileAction; - property Action updateProfileAction; - property Action resetProfileAction; - property Action manageProfilesAction; - property Action configureSettingsAction; property int currentModeIndex; color: UM.Theme.getColor("sidebar"); @@ -52,20 +45,8 @@ Rectangle width: parent.width height: totalHeightHeader - addMachineAction: base.addMachineAction; - configureMachinesAction: base.configureMachinesAction; - } - - ProfileSetup { - id: profileItem - addProfileAction: base.addProfileAction - updateProfileAction: base.updateProfileAction - resetProfileAction: base.resetProfileAction - manageProfilesAction: base.manageProfilesAction - anchors.top: header.bottom + anchors.top: parent.top anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - height: totalHeightProfileSetup onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() @@ -76,7 +57,7 @@ Rectangle width: parent.width height: UM.Theme.getSize("sidebar_lining").height color: UM.Theme.getColor("sidebar_lining") - anchors.top: profileItem.bottom + anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 282fa8af25..282068ee2c 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -8,54 +8,54 @@ import QtQuick.Controls.Styles 1.1 import UM 1.2 as UM import Cura 1.0 as Cura -Item +Column { id: base; - // Machine Setup - property Action addMachineAction; - property Action configureMachinesAction; - UM.I18nCatalog { id: catalog; name:"cura"} + property int totalHeightHeader: childrenRect.height property int currentExtruderIndex; - Rectangle { - id: sidebarTabRow - width: base.width - height: 0 - anchors.top: parent.top - color: UM.Theme.getColor("sidebar_header_bar") - } + spacing: UM.Theme.getSize("default_margin").height - Rectangle { + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + + Row + { id: machineSelectionRow - width: base.width height: UM.Theme.getSize("sidebar_setup").height - anchors.top: sidebarTabRow.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.horizontalCenter: parent.horizontalCenter - Label{ + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + Label + { id: machineSelectionLabel - //: Machine selection label - text: catalog.i18nc("@label:listbox","Printer:"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@label:listbox", "Printer:"); anchors.verticalCenter: parent.verticalCenter font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); + + width: parent.width * 0.45 - UM.Theme.getSize("default_margin").width } - ToolButton { + ToolButton + { id: machineSelection text: Cura.MachineManager.activeMachineName; - width: parent.width/100*55 + height: UM.Theme.getSize("setting_control").height tooltip: Cura.MachineManager.activeMachineName; - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter style: UM.Theme.styles.sidebar_header_button + width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width + menu: Menu { id: machineSelectionMenu @@ -81,119 +81,114 @@ Item MenuSeparator { } - MenuItem { action: base.addMachineAction; } - MenuItem { action: base.configureMachinesAction; } + MenuItem { action: Cura.Actions.addMachine; } + MenuItem { action: Cura.Actions.configureMachines; } } } } - Rectangle { - id: extruderSelection - width: parent.width/100*55 + ListView + { + id: extrudersList + property var index: 0 + 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: 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 + height: UM.Theme.getSize("sidebar_header_mode_toggle").height - 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; - } + boundsBehavior: Flickable.StopAtBounds + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + ExclusiveGroup { id: extruderMenuGroup; } + + orientation: ListView.Horizontal + + model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: true } + + delegate: Button + { + height: ListView.view.height + width: ListView.view.width / extrudersModel.rowCount() + + text: model.name + exclusiveGroup: extruderMenuGroup; + checkable: true; + checked: base.currentExtruderIndex == index + + onClicked: + { + focus = true; //Changing focus applies the currently-being-typed values so it can change the displayed setting values. + base.currentExtruderIndex = index; + ExtruderManager.setActiveExtruderIndex(index); + } + + 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; } - label: Item { } } + label: Item { } } } - 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 + Row { - 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: variantRow - anchors.top: extruderSelection.visible ? extruderSelection.bottom : machineSelectionRow.bottom - anchors.topMargin: visible ? UM.Theme.getSize("default_margin").height : 0 - width: base.width - height: visible ? UM.Theme.getSize("sidebar_setup").height : 0 + + height: UM.Theme.getSize("sidebar_setup").height visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials - Label{ + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + Label + { 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.verticalCenter: parent.verticalCenter - width: parent.width/100*45 + width: parent.width * 0.45 - UM.Theme.getSize("default_margin").width font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } Rectangle { - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - width: parent.width/100*55 + width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("setting_control").height ToolButton { @@ -230,13 +225,6 @@ Item 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) @@ -266,7 +254,23 @@ Item id: materialSelectionInstantiator model: UM.InstanceContainersModel { - filter: { "type": "material", "definition": Cura.MachineManager.activeDefinitionId } + filter: + { + var result = { "type": "material" } + if(Cura.MachineManager.filterMaterialsByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId + if(Cura.MachineManager.hasVariants) + { + result.variant = Cura.MachineManager.activeVariantId + } + } + else + { + result.definition = "fdmprinter" + } + return result + } } MenuItem { @@ -277,13 +281,6 @@ Item 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) @@ -296,6 +293,147 @@ Item } } + Row + { + id: globalProfileRow + height: UM.Theme.getSize("sidebar_setup").height + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + + Label + { + id: globalProfileLabel + text: catalog.i18nc("@label","Profile:"); + width: parent.width * 0.45 - UM.Theme.getSize("default_margin").width + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: globalProfileSelection + text: Cura.MachineManager.activeQualityName + width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("setting_control").height + tooltip: Cura.MachineManager.activeQualityName + style: UM.Theme.styles.sidebar_header_button + + menu: Menu + { + id: profileSelectionMenu + Instantiator + { + id: profileSelectionInstantiator + model: UM.InstanceContainersModel + { + filter: + { + var result = { "type": "quality" }; + if(Cura.MachineManager.filterQualityByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId; + if(Cura.MachineManager.hasMaterials) + { + result.material = Cura.MachineManager.activeMaterialId; + } + } + else + { + result.definition = "fdmprinter" + } + return result + } + } + property int separatorIndex: -1 + + Loader { + property QtObject model_data: model + property int model_index: index + sourceComponent: menuItemDelegate + } + onObjectAdded: + { + //Insert a separator between readonly and custom profiles + if(separatorIndex < 0 && index > 0) + { + if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) + { + profileSelectionMenu.insertSeparator(index); + separatorIndex = index; + } + } + //Because of the separator, custom profiles move one index lower + profileSelectionMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item); + } + onObjectRemoved: + { + //When adding a profile, the menu is rebuilt by removing all items. + //If a separator was added, we need to remove that too. + if(separatorIndex >= 0) + { + profileSelectionMenu.removeItem(profileSelectionMenu.items[separatorIndex]) + separatorIndex = -1; + } + profileSelectionMenu.removeItem(object.item); + } + } + ExclusiveGroup { id: profileSelectionMenuGroup; } + + Component + { + id: menuItemDelegate + MenuItem + { + id: item + text: model_data ? model_data.name : "" + checkable: true + checked: model_data != null ? Cura.MachineManager.activeQualityId == model_data.id : false + exclusiveGroup: profileSelectionMenuGroup; + onTriggered: Cura.MachineManager.setActiveQuality(model_data.id) + } + } + + MenuSeparator { } + MenuItem { action: Cura.Actions.addProfile } + MenuItem { action: Cura.Actions.updateProfile } + MenuItem { action: Cura.Actions.resetProfile } + MenuSeparator { } + MenuItem { action: Cura.Actions.manageProfiles } + } + + UM.SimpleButton + { + id: customisedSettings + + visible: Cura.MachineManager.hasUserSettings + height: parent.height * 0.6 + width: parent.height * 0.6 + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("default_margin").width + + color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); + iconSource: UM.Theme.getIcon("star"); + + onClicked: Cura.Actions.manageProfiles.trigger() + onEntered: + { + var content = catalog.i18nc("@tooltip","Some setting values are different from the values stored in the profile.\n\nClick to open the profile manager.") + base.showTooltip(globalProfileRow, Qt.point(0, globalProfileRow.height / 2), content) + } + onExited: base.hideTooltip() + } + } + } + UM.SettingPropertyProvider { id: machineExtruderCount @@ -305,4 +443,6 @@ Item watchedProperties: [ "value" ] storeIndex: 0 } + + UM.I18nCatalog { id: catalog; name:"cura" } } diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index c828b5d3db..80870ac29f 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -28,13 +28,13 @@ Item id: infillCellLeft anchors.top: parent.top anchors.left: parent.left - width: base.width/100* 35 - UM.Theme.getSize("default_margin").width + width: base.width / 100 * 35 - UM.Theme.getSize("default_margin").width height: childrenRect.height Label{ id: infillLabel //: Infill selection label - text: catalog.i18nc("@label","Infill:"); + text: catalog.i18nc("@label", "Infill:"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); anchors.top: parent.top @@ -85,7 +85,7 @@ Item { return UM.Theme.getColor("setting_control_selected") } - else if(mousearea.containsMouse) + else if(infillMouseArea.containsMouse) { return UM.Theme.getColor("setting_control_border_highlight") } @@ -106,7 +106,7 @@ Item } MouseArea { - id: mousearea + id: infillMouseArea anchors.fill: parent hoverEnabled: true onClicked: { @@ -187,28 +187,29 @@ Item anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: brimCheckBox.verticalCenter - width: parent.width/100*35 - 3 * UM.Theme.getSize("default_margin").width + width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width //: Bed adhesion label - text: catalog.i18nc("@label:listbox","Bed Adhesion:"); + text: catalog.i18nc("@label:listbox", "Bed Adhesion:"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } CheckBox{ id: brimCheckBox - property bool hovered_ex: false + property alias _hovered: brimMouseArea.containsMouse anchors.top: parent.top anchors.left: adhesionHelperLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width //: Setting enable skirt adhesion checkbox - text: catalog.i18nc("@option:check","Print Brim"); + text: catalog.i18nc("@option:check", "Print Brim"); style: UM.Theme.styles.checkbox; checked: platformAdhesionType.properties.value == "brim" MouseArea { + id: brimMouseArea anchors.fill: parent hoverEnabled: true onClicked: @@ -217,13 +218,11 @@ Item } onEntered: { - parent.hovered_ex = true 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: { - parent.hovered_ex = false base.hideTooltip(); } } @@ -234,9 +233,9 @@ Item 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 + width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width //: Support label - text: catalog.i18nc("@label:listbox","Support:"); + text: catalog.i18nc("@label:listbox", "Support:"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } @@ -244,7 +243,7 @@ Item CheckBox{ id: supportCheckBox visible: machineExtruderCount.properties.value <= 1 - property bool hovered_ex: false + property alias _hovered: supportMouseArea.containsMouse anchors.top: brimCheckBox.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height @@ -252,11 +251,12 @@ Item anchors.leftMargin: UM.Theme.getSize("default_margin").width //: Setting enable support checkbox - text: catalog.i18nc("@option:check","Print Support Structure"); + text: catalog.i18nc("@option:check", "Print Support Structure"); style: UM.Theme.styles.checkbox; checked: supportEnabled.properties.value == "True" MouseArea { + id: supportMouseArea anchors.fill: parent hoverEnabled: true onClicked: @@ -265,13 +265,11 @@ Item } onEntered: { - parent.hovered_ex = true 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: { - parent.hovered_ex = false base.hideTooltip(); } } @@ -286,10 +284,10 @@ Item 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 + width: parent.width / 100 * 45 style: UM.Theme.styles.combobox - property bool hovered_ex: false + property alias _hovered: supportExtruderMouseArea.containsMouse currentIndex: supportEnabled.properties.value == "True" ? parseFloat(supportExtruderNr.properties.value) + 1 : 0 onActivated: { @@ -301,18 +299,17 @@ Item } } MouseArea { + id: supportExtruderMouseArea 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(); } } @@ -358,7 +355,7 @@ Item anchors.rightMargin: UM.Theme.getSize("default_margin").width wrapMode: Text.WordWrap //: Tips label - text: catalog.i18nc("@label","Need help improving your prints? Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting"); + text: catalog.i18nc("@label", "Need help improving your prints? Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); linkColor: UM.Theme.getColor("text_link") @@ -374,8 +371,6 @@ Item key: "infill_sparse_density" watchedProperties: [ "value" ] storeIndex: 0 - - onPropertiesChanged: console.log(properties.value) } UM.SettingPropertyProvider diff --git a/resources/qml/ViewPage.qml b/resources/qml/ViewPage.qml index 7d840f7ab0..baf56db116 100644 --- a/resources/qml/ViewPage.qml +++ b/resources/qml/ViewPage.qml @@ -19,6 +19,7 @@ UM.PreferencesPage { UM.Preferences.resetPreference("view/show_overhang"); UM.Preferences.resetPreference("view/center_on_select"); + UM.Preferences.resetPreference("view/top_layer_count"); } Column @@ -57,12 +58,38 @@ UM.PreferencesPage } } + UM.TooltipArea { + width: childrenRect.width; + height: childrenRect.height; + text: catalog.i18nc("@info:tooltip","Display 5 top layers in layer view or only the top-most layer. Rendering 5 layers takes longer, but may show more information.") + + CheckBox + { + id: topLayerCheckbox + text: catalog.i18nc("@action:button","Display five top layers in layer view."); + checked: UM.Preferences.getValue("view/top_layer_count") == 5 + onClicked: + { + if(UM.Preferences.getValue("view/top_layer_count") == 5) + { + UM.Preferences.setValue("view/top_layer_count", 1) + } + else + { + UM.Preferences.setValue("view/top_layer_count", 5) + } + } + } + } + Connections { target: UM.Preferences onPreferenceChanged: { overhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang")) centerCheckbox.checked = boolCheck(UM.Preferences.getValue("view/center_on_select")) + topLayerCheckbox = UM.Preferences.getValue("view/top_layer_count") == 5 + } } } diff --git a/resources/qml/qmldir b/resources/qml/qmldir deleted file mode 100644 index 096561aca5..0000000000 --- a/resources/qml/qmldir +++ /dev/null @@ -1,3 +0,0 @@ -module Cura - -singleton Actions 1.0 Actions.qml diff --git a/resources/quality/high.inst.cfg b/resources/quality/high.inst.cfg index 0329e9ffe1..b4498c6c8b 100644 --- a/resources/quality/high.inst.cfg +++ b/resources/quality/high.inst.cfg @@ -5,6 +5,9 @@ definition = fdmprinter [metadata] type = quality +weight = -3 [values] layer_height = 0.06 +speed_topbottom = 15 +speed_infill = 80 diff --git a/resources/profiles/general/Low+Quality.cfg b/resources/quality/low.inst.cfg similarity index 74% rename from resources/profiles/general/Low+Quality.cfg rename to resources/quality/low.inst.cfg index f7f4e5d6b7..d34a7c6461 100644 --- a/resources/profiles/general/Low+Quality.cfg +++ b/resources/quality/low.inst.cfg @@ -1,9 +1,13 @@ [general] -version = 1 +version = 2 name = Low Quality +definition = fdmprinter + +[metadata] +type = quality weight = -1 -[settings] +[values] infill_sparse_density = 10 layer_height = 0.15 cool_min_layer_time = 3 @@ -12,4 +16,3 @@ speed_wall_x = 80 speed_infill = 100 wall_thickness = 1 speed_topbottom = 30 - diff --git a/resources/quality/normal.inst.cfg b/resources/quality/normal.inst.cfg index 6d317cdf7a..417c7c700f 100644 --- a/resources/quality/normal.inst.cfg +++ b/resources/quality/normal.inst.cfg @@ -5,5 +5,6 @@ definition = fdmprinter [metadata] type = quality +weight = -2 [values] diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg new file mode 100644 index 0000000000..11b926378d --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg @@ -0,0 +1,22 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.25_mm +weight = -2 + +[values] +layer_height = 0.06 +wall_thickness = 0.88 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +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/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg new file mode 100644 index 0000000000..bdeeb935f4 --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.4_mm +weight = -1 + +[values] +layer_height = 0.15 +wall_thickness = 0.7 +top_bottom_thickness = 0.75 +infill_sparse_density = 18 +speed_print = 55 +speed_wall = 40 +speed_topbottom = 30 +speed_travel = 150 +speed_layer_0 = 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/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg new file mode 100644 index 0000000000..d658ee5bb5 --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg @@ -0,0 +1,22 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.4_mm +weight = -3 + +[values] +layer_height = 0.06 +wall_thickness = 1.05 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +speed_print = 45 +speed_wall = 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/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg new file mode 100644 index 0000000000..ff024ccc69 --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg @@ -0,0 +1,21 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.4_mm +weight = -2 + +[values] +layer_height = 0.1 +wall_thickness = 1.05 +top_bottom_thickness = 0.8 +infill_sparse_density = 20 +speed_print = 45 +speed_wall = 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/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg new file mode 100644 index 0000000000..c2f4daa86f --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg @@ -0,0 +1,23 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.6_mm +weight = -2 + +[values] +layer_height = 0.15 +wall_thickness = 1.59 +top_bottom_thickness = 1.2 +infill_sparse_density = 20 +speed_print = 40 +speed_infill = 55 +cool_min_layer_time = 3 +cool_fan_speed_min = 50 +cool_min_speed = 20 +cool_min_layer_time_fan_speed_max = 20 + + diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg new file mode 100644 index 0000000000..362e844214 --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg @@ -0,0 +1,21 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_extended_plus_0.8_mm +weight = -2 + +[values] +layer_height = 0.2 +wall_thickness = 2.1 +top_bottom_thickness = 1.2 +infill_sparse_density = 20 +speed_print = 40 +cool_min_layer_time = 3 +cool_fan_speed_min = 50 +cool_min_speed = 15 +cool_min_layer_time_fan_speed_max = 25 + diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg new file mode 100644 index 0000000000..f4d9264d3e --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg @@ -0,0 +1,21 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.25_mm +weight = -2 + +[values] +layer_height = 0.06 +wall_thickness = 0.88 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +speed_print = 30 +cool_min_layer_time = 2 +cool_fan_speed_min = 20 +cool_min_speed = 15 +cool_min_layer_time_fan_speed_max = 15 + diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg new file mode 100644 index 0000000000..f514f22294 --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.4_mm +weight = -1 + +[values] +layer_height = 0.15 +wall_thickness = 0.7 +top_bottom_thickness = 0.75 +infill_sparse_density = 18 +speed_print = 45 +speed_wall = 40 +speed_travel = 150 +speed_layer_0 = 30 +cool_min_layer_time = 3 +cool_fan_speed_min = 80 +cool_min_speed = 10 +cool_min_layer_time_fan_speed_max = 15 + + diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg new file mode 100644 index 0000000000..0c68be2f5f --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg @@ -0,0 +1,21 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.4_mm +weight = -3 + +[values] +layer_height = 0.06 +wall_thickness = 1.05 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +speed_print = 45 +speed_wall = 30 +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/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg new file mode 100644 index 0000000000..26ea8ce9bc --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg @@ -0,0 +1,22 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.4_mm +weight = -2 + +[values] +layer_height = 0.1 +wall_thickness = 1.05 +top_bottom_thickness = 0.8 +infill_sparse_density = 20 +speed_print = 45 +speed_wall = 30 +cool_min_layer_time = 3 +cool_fan_speed_min = 80 +cool_min_speed = 10 +cool_min_layer_time_fan_speed_max = 15 + diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg new file mode 100644 index 0000000000..d6d10dbe1a --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg @@ -0,0 +1,20 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.6_mm +weight = -2 + +[values] +layer_height = 0.15 +wall_thickness = 1.59 +top_bottom_thickness = 1.2 +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/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg new file mode 100644 index 0000000000..53d5e0bc8c --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg @@ -0,0 +1,20 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_extended_plus_0.8_mm +weight = -2 + +[values] +layer_height = 0.2 +wall_thickness = 2.1 +top_bottom_thickness = 1.2 +infill_sparse_density = 20 +speed_print = 40 +cool_min_layer_time = 3 +cool_fan_speed_min = 80 +cool_min_speed = 8 +cool_min_layer_time_fan_speed_max = 25 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg new file mode 100644 index 0000000000..5e54b3194a --- /dev/null +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg @@ -0,0 +1,18 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_extended_plus_0.25_mm +weight = -2 + +[values] +layer_height = 0.06 +wall_thickness = 0.88 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +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/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg similarity index 62% rename from resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile rename to resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg index 06e401c139..893256bb33 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_fast.curaprofile +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = PLA +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -1 -[settings] +[values] layer_height = 0.15 wall_thickness = 0.7 top_bottom_thickness = 0.75 diff --git a/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg similarity index 58% rename from resources/profiles/ultimaker2+/pla_0.4_high.curaprofile rename to resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg index 5e2f762354..844e2b3eac 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_high.curaprofile +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = PLA +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -3 -[settings] +[values] layer_height = 0.06 wall_thickness = 1.05 top_bottom_thickness = 0.72 diff --git a/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg similarity index 58% rename from resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile rename to resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg index 689a3251b2..47d511446a 100644 --- a/resources/profiles/ultimaker2+/pla_0.4_normal.curaprofile +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = PLA +definition = ultimaker2_extended_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -2 -[settings] +[values] layer_height = 0.1 wall_thickness = 1.05 top_bottom_thickness = 0.8 diff --git a/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg similarity index 58% rename from resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile rename to resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg index 188ed42a95..a2b15b6f16 100644 --- a/resources/profiles/ultimaker2+/pla_0.6_normal.curaprofile +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.6 mm -material = PLA +definition = ultimaker2_extended_plus + +[metadata] +material = generic_pla_ultimaker2_extended_plus_0.6_mm +type = quality weight = -2 -[settings] +[values] layer_height = 0.15 wall_thickness = 1.59 top_bottom_thickness = 1.2 diff --git a/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg similarity index 58% rename from resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile rename to resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg index 92cb4a6054..08b5bec667 100644 --- a/resources/profiles/ultimaker2+/pla_0.8_normal.curaprofile +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.8 mm -material = PLA +definition = ultimaker2_extended_plus + +[metadata] +material = generic_pla_ultimaker2_extended_plus_0.8_mm +type = quality weight = -2 -[settings] +[values] layer_height = 0.2 wall_thickness = 2.1 top_bottom_thickness = 1.2 diff --git a/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg similarity index 61% rename from resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile rename to resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg index 63c1fc9fdd..9a44582610 100644 --- a/resources/profiles/ultimaker2+/pla_0.25_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.25 mm -material = PLA +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_plus_0.25_mm weight = -2 -[settings] +[values] layer_height = 0.06 wall_thickness = 0.88 top_bottom_thickness = 0.72 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg new file mode 100644 index 0000000000..0df882e418 --- /dev/null +++ b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg @@ -0,0 +1,22 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_plus_0.4_mm +weight = -1 + +[values] +layer_height = 0.15 +wall_thickness = 0.7 +top_bottom_thickness = 0.75 +infill_sparse_density = 18 +speed_print = 60 +speed_wall = 50 +speed_topbottom = 30 +speed_travel = 150 +speed_layer_0 = 30 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg new file mode 100644 index 0000000000..a8abdb081f --- /dev/null +++ b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg @@ -0,0 +1,19 @@ +[general] +version = 2 +name = High Quality +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_plus_0.4_mm +weight = -3 + +[values] +layer_height = 0.06 +wall_thickness = 1.05 +top_bottom_thickness = 0.72 +infill_sparse_density = 22 +speed_print = 50 +speed_topbottom = 20 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg new file mode 100644 index 0000000000..c29b017bbe --- /dev/null +++ b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg @@ -0,0 +1,19 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_pla_ultimaker2_plus_0.4_mm +weight = -2 + +[values] +layer_height = 0.1 +wall_thickness = 1.05 +top_bottom_thickness = 0.8 +infill_sparse_density = 20 +speed_print = 50 +speed_topbottom = 20 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg new file mode 100644 index 0000000000..5a0a840ae7 --- /dev/null +++ b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg @@ -0,0 +1,21 @@ +[general] +version = 2 +name = Normal Quality +definition = ultimaker2_plus + +[metadata] +material = generic_pla_ultimaker2_plus_0.6_mm +type = quality +weight = -2 + +[values] +layer_height = 0.15 +wall_thickness = 1.59 +top_bottom_thickness = 1.2 +infill_sparse_density = 20 +speed_print = 55 +speed_wall = 40 +speed_wall_0 = 25 +speed_topbottom = 20 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg new file mode 100644 index 0000000000..bd90b8c059 --- /dev/null +++ b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg @@ -0,0 +1,19 @@ +[general] +version = 2 +name = Fast Print +definition = ultimaker2_plus + +[metadata] +material = generic_pla_ultimaker2_plus_0.8_mm +type = quality +weight = -2 + +[values] +layer_height = 0.2 +wall_thickness = 2.1 +top_bottom_thickness = 1.2 +infill_sparse_density = 20 +speed_print = 40 +speed_wall_0 = 25 +cool_min_layer_time = 5 +cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg similarity index 68% rename from resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg index 9f45e9d01a..05a9bce71c 100644 --- a/resources/profiles/ultimaker2+/abs_0.25_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.25 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.25_mm weight = -2 -[settings] +[values] layer_height = 0.06 wall_thickness = 0.88 top_bottom_thickness = 0.72 diff --git a/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg index 50018372b5..cd0a25981a 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_fast.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg @@ -1,17 +1,21 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.4_mm weight = -1 -[settings] +[values] layer_height = 0.15 wall_thickness = 0.7 top_bottom_thickness = 0.75 infill_sparse_density = 18 speed_print = 55 +speed_wall = 40 +speed_topbottom = 30 speed_travel = 150 speed_layer_0 = 30 cool_min_layer_time = 3 diff --git a/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/abs_0.4_high.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg index 341c9cc34f..4b1ece31ef 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_high.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.4_mm weight = -3 -[settings] +[values] layer_height = 0.06 wall_thickness = 1.05 top_bottom_thickness = 0.72 infill_sparse_density = 22 speed_print = 45 +speed_wall = 30 cool_min_layer_time = 3 cool_fan_speed_min = 20 cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg index d8fce8a4dd..f1b01fd408 100644 --- a/resources/profiles/ultimaker2+/abs_0.4_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.4_mm weight = -2 -[settings] +[values] layer_height = 0.1 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 speed_print = 45 +speed_wall = 30 cool_min_layer_time = 3 cool_fan_speed_min = 20 cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg index 5512450471..89e73dda38 100644 --- a/resources/profiles/ultimaker2+/abs_0.6_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.6 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.6_mm weight = -2 -[settings] +[values] layer_height = 0.15 wall_thickness = 1.59 top_bottom_thickness = 1.2 infill_sparse_density = 20 speed_print = 40 +speed_infill = 55 cool_min_layer_time = 3 cool_fan_speed_min = 50 cool_min_speed = 20 diff --git a/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg similarity index 67% rename from resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg index e5f27c51a2..2171fd3837 100644 --- a/resources/profiles/ultimaker2+/abs_0.8_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.8 mm -material = ABS +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_abs_ultimaker2_plus_0.8_mm weight = -2 -[settings] +[values] layer_height = 0.2 wall_thickness = 2.1 top_bottom_thickness = 1.2 diff --git a/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg similarity index 68% rename from resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg index c4c09932d8..6a300ba27d 100644 --- a/resources/profiles/ultimaker2+/cpe_0.25_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.25 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.25_mm weight = -2 -[settings] +[values] layer_height = 0.06 wall_thickness = 0.88 top_bottom_thickness = 0.72 diff --git a/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg similarity index 68% rename from resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg index f9050e5ce5..e76c5205f5 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_fast.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.4_mm weight = -1 -[settings] +[values] layer_height = 0.15 wall_thickness = 0.7 top_bottom_thickness = 0.75 infill_sparse_density = 18 speed_print = 45 +speed_wall = 40 speed_travel = 150 speed_layer_0 = 30 cool_min_layer_time = 3 diff --git a/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg index 377ab5b257..60f171dc17 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_high.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = High Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.4_mm weight = -3 -[settings] +[values] layer_height = 0.06 wall_thickness = 1.05 top_bottom_thickness = 0.72 infill_sparse_density = 22 speed_print = 45 +speed_wall = 30 cool_min_layer_time = 2 cool_fan_speed_min = 80 cool_min_speed = 15 diff --git a/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg similarity index 65% rename from resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg index e8142405ff..04116dbe21 100644 --- a/resources/profiles/ultimaker2+/cpe_0.4_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg @@ -1,17 +1,20 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.4 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.4_mm weight = -2 -[settings] +[values] layer_height = 0.1 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 speed_print = 45 +speed_wall = 30 cool_min_layer_time = 3 cool_fan_speed_min = 80 cool_min_speed = 10 diff --git a/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg similarity index 68% rename from resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg index 034fa17e1b..35e666e6d9 100644 --- a/resources/profiles/ultimaker2+/cpe_0.6_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Normal Quality -machine_type = ultimaker2plus -machine_variant = 0.6 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.6_mm weight = -2 -[settings] +[values] layer_height = 0.15 wall_thickness = 1.59 top_bottom_thickness = 1.2 diff --git a/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg similarity index 67% rename from resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile rename to resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg index 523a5d3243..c36d1714a0 100644 --- a/resources/profiles/ultimaker2+/cpe_0.8_normal.curaprofile +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg @@ -1,12 +1,14 @@ [general] -version = 1 +version = 2 name = Fast Print -machine_type = ultimaker2plus -machine_variant = 0.8 mm -material = CPE +definition = ultimaker2_plus + +[metadata] +type = quality +material = generic_cpe_ultimaker2_plus_0.8_mm weight = -2 -[settings] +[values] layer_height = 0.2 wall_thickness = 2.1 top_bottom_thickness = 1.2 diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index b2c2329169..1428c3d40a 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -283,14 +283,14 @@ QtObject { property Component combobox: Component { ComboBoxStyle { background: Rectangle { - implicitHeight: UM.Theme.getSize("setting_control").height; - implicitWidth: UM.Theme.getSize("setting_control").width; + implicitHeight: Theme.getSize("setting_control").height; + implicitWidth: Theme.getSize("setting_control").width; - color: (control.hovered || control.hovered_ex) ? Theme.getColor("setting_control_highlight") : Theme.getColor("setting_control"); + color: (control.hovered || control._hovered) ? Theme.getColor("setting_control_highlight") : Theme.getColor("setting_control"); Behavior on color { ColorAnimation { duration: 50; } } 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"); + border.color: (control.hovered || control._hovered) ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border"); } label: Item { Label { @@ -301,7 +301,7 @@ QtObject { anchors.verticalCenter: parent.verticalCenter; text: control.currentText; - font: UM.Theme.getFont("default"); + font: Theme.getFont("default"); color: !enabled ? Theme.getColor("setting_control_disabled_text") : Theme.getColor("setting_control_text"); elide: Text.ElideRight; @@ -314,9 +314,9 @@ QtObject { 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 + source: Theme.getIcon("arrow_bottom") + width: Theme.getSize("standard_arrow").width + height: Theme.getSize("standard_arrow").height sourceSize.width: width + 5 sourceSize.height: width + 5 @@ -333,13 +333,13 @@ QtObject { implicitWidth: Theme.getSize("checkbox").width; implicitHeight: Theme.getSize("checkbox").height; - color: (control.hovered || control.hovered_ex) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); Behavior on color { ColorAnimation { duration: 50; } } radius: control.exclusiveGroup ? Theme.getSize("checkbox").width / 2 : 0 border.width: Theme.getSize("default_lining").width; - border.color: (control.hovered || control.hovered_ex) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); + border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); UM.RecolorImage { anchors.verticalCenter: parent.verticalCenter diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 67df41ef7c..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],