Merge remote-tracking branch 'origin/CURA-6522_one_at_a_time_overlapping_build_area' into CURA-6522_one_at_a_time_overlapping_build_area

This commit is contained in:
Nino van Hooff 2019-12-04 14:28:48 +01:00
commit b30b641034
1350 changed files with 16424 additions and 11601 deletions

View file

@ -9,7 +9,11 @@ DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
DEFAULT_CURA_VERSION = "master" DEFAULT_CURA_VERSION = "master"
DEFAULT_CURA_BUILD_TYPE = "" DEFAULT_CURA_BUILD_TYPE = ""
DEFAULT_CURA_DEBUG_MODE = False DEFAULT_CURA_DEBUG_MODE = False
DEFAULT_CURA_SDK_VERSION = "7.0.0"
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
# CuraVersion.py.in template.
CuraSDKVersion = "7.0.0"
try: try:
from cura.CuraVersion import CuraAppName # type: ignore from cura.CuraVersion import CuraAppName # type: ignore
@ -32,6 +36,9 @@ try:
except ImportError: except ImportError:
CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value] CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value]
# CURA-6569
# This string indicates what type of version it is. For example, "enterprise". By default it's empty which indicates
# a default/normal Cura build.
try: try:
from cura.CuraVersion import CuraBuildType # type: ignore from cura.CuraVersion import CuraBuildType # type: ignore
except ImportError: except ImportError:
@ -42,7 +49,7 @@ try:
except ImportError: except ImportError:
CuraDebugMode = DEFAULT_CURA_DEBUG_MODE CuraDebugMode = DEFAULT_CURA_DEBUG_MODE
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # CURA-6569
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # Various convenience flags indicating what kind of Cura build it is.
# CuraVersion.py.in template. __ENTERPRISE_VERSION_TYPE = "enterprise"
CuraSDKVersion = "7.0.0" IsEnterpriseVersion = CuraBuildType.lower() == __ENTERPRISE_VERSION_TYPE

View file

@ -26,7 +26,6 @@ catalog = i18nCatalog("cura")
import numpy import numpy
import math import math
import copy
from typing import List, Optional, TYPE_CHECKING, Any, Set, cast, Iterable, Dict from typing import List, Optional, TYPE_CHECKING, Any, Set, cast, Iterable, Dict

View file

@ -145,7 +145,7 @@ class CuraApplication(QtApplication):
# SettingVersion represents the set of settings available in the machine/extruder definitions. # SettingVersion represents the set of settings available in the machine/extruder definitions.
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible # You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
# changes of the settings. # changes of the settings.
SettingVersion = 10 SettingVersion = 11
Created = False Created = False
@ -720,6 +720,8 @@ class CuraApplication(QtApplication):
## Handle loading of all plugin types (and the backend explicitly) ## Handle loading of all plugin types (and the backend explicitly)
# \sa PluginRegistry # \sa PluginRegistry
def _loadPlugins(self) -> None: def _loadPlugins(self) -> None:
self._plugin_registry.setCheckIfTrusted(ApplicationMetadata.IsEnterpriseVersion)
self._plugin_registry.addType("profile_reader", self._addProfileReader) self._plugin_registry.addType("profile_reader", self._addProfileReader)
self._plugin_registry.addType("profile_writer", self._addProfileWriter) self._plugin_registry.addType("profile_writer", self._addProfileWriter)
@ -1368,16 +1370,19 @@ class CuraApplication(QtApplication):
for node in nodes: for node in nodes:
mesh_data = node.getMeshData() mesh_data = node.getMeshData()
if mesh_data and mesh_data.getFileName():
job = ReadMeshJob(mesh_data.getFileName())
job._node = node # type: ignore
job.finished.connect(self._reloadMeshFinished)
if has_merged_nodes:
job.finished.connect(self.updateOriginOfMergedMeshes)
job.start() if mesh_data:
else: file_name = mesh_data.getFileName()
Logger.log("w", "Unable to reload data because we don't have a filename.") if file_name:
job = ReadMeshJob(file_name)
job._node = node # type: ignore
job.finished.connect(self._reloadMeshFinished)
if has_merged_nodes:
job.finished.connect(self.updateOriginOfMergedMeshes)
job.start()
else:
Logger.log("w", "Unable to reload data because we don't have a filename.")
@pyqtSlot("QStringList") @pyqtSlot("QStringList")
def setExpandedCategories(self, categories: List[str]) -> None: def setExpandedCategories(self, categories: List[str]) -> None:
@ -1796,7 +1801,7 @@ class CuraApplication(QtApplication):
try: try:
result = workspace_reader.preRead(file_path, show_dialog=False) result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted return result == WorkspaceReader.PreReadResult.accepted
except Exception as e: except Exception:
Logger.logException("e", "Could not check file %s", file_url) Logger.logException("e", "Could not check file %s", file_url)
return False return False
@ -1892,3 +1897,7 @@ class CuraApplication(QtApplication):
op.push() op.push()
from UM.Scene.Selection import Selection from UM.Scene.Selection import Selection
Selection.clear() Selection.clear()
@classmethod
def getInstance(cls, *args, **kwargs) -> "CuraApplication":
return cast(CuraApplication, super().getInstance(**kwargs))

View file

@ -1,7 +1,7 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application from UM.Qt.QtApplication import QtApplication
from typing import Any, Optional from typing import Any, Optional
import numpy import numpy
@ -232,7 +232,7 @@ class LayerPolygon:
@classmethod @classmethod
def getColorMap(cls): def getColorMap(cls):
if cls.__color_map is None: if cls.__color_map is None:
theme = Application.getInstance().getTheme() theme = QtApplication.getInstance().getTheme()
cls.__color_map = numpy.array([ cls.__color_map = numpy.array([
theme.getColor("layerview_none").getRgbF(), # NoneType theme.getColor("layerview_none").getRgbF(), # NoneType
theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type

View file

@ -140,7 +140,7 @@ class MachineNode(ContainerNode):
elif groups_by_name[name].intent_category == "default": # Intent category should be stored as "default" if everything is default or as the intent if any of the extruder have an actual intent. elif groups_by_name[name].intent_category == "default": # Intent category should be stored as "default" if everything is default or as the intent if any of the extruder have an actual intent.
groups_by_name[name].intent_category = quality_changes.get("intent_category", "default") groups_by_name[name].intent_category = quality_changes.get("intent_category", "default")
if "position" in quality_changes: # An extruder profile. if quality_changes.get("position") is not None: # An extruder profile.
groups_by_name[name].metadata_per_extruder[int(quality_changes["position"])] = quality_changes groups_by_name[name].metadata_per_extruder[int(quality_changes["position"])] = quality_changes
else: # Global profile. else: # Global profile.
groups_by_name[name].metadata_for_global = quality_changes groups_by_name[name].metadata_for_global = quality_changes

View file

@ -6,6 +6,7 @@ from typing import Dict, Set
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.Logger import Logger
import cura.CuraApplication # Imported like this to prevent a circular reference. import cura.CuraApplication # Imported like this to prevent a circular reference.
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
@ -153,7 +154,12 @@ class BaseMaterialsModel(ListModel):
if not extruder_stack: if not extruder_stack:
return return
nozzle_name = extruder_stack.variant.getName() nozzle_name = extruder_stack.variant.getName()
materials = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[nozzle_name].materials machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
if nozzle_name not in machine_node.variants:
Logger.log("w", "Unable to find variant %s in container tree", nozzle_name)
self._available_materials = {}
return
materials = machine_node.variants[nozzle_name].materials
approximate_material_diameter = extruder_stack.getApproximateMaterialDiameter() approximate_material_diameter = extruder_stack.getApproximateMaterialDiameter()
self._available_materials = {key: material for key, material in materials.items() if float(material.getMetaDataEntry("approximate_diameter", -1)) == approximate_material_diameter} self._available_materials = {key: material for key, material in materials.items() if float(material.getMetaDataEntry("approximate_diameter", -1)) == approximate_material_diameter}

View file

@ -2,13 +2,8 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from UM.Application import Application
from UM.Logger import Logger from UM.Logger import Logger
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.Util import parseBool
from cura.Machines.VariantType import VariantType
class BuildPlateModel(ListModel): class BuildPlateModel(ListModel):
@ -26,4 +21,4 @@ class BuildPlateModel(ListModel):
def _update(self): def _update(self):
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
self.setItems([]) self.setItems([])
return return

View file

@ -11,7 +11,6 @@ from UM.Util import parseBool
from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt
if TYPE_CHECKING: if TYPE_CHECKING:
from PyQt5.QtCore import QObject
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice

View file

@ -1,9 +1,10 @@
#Copyright (c) 2019 Ultimaker B.V. #Copyright (c) 2019 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher. #Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, QTimer
import collections import collections
from PyQt5.QtCore import Qt, QTimer
from typing import TYPE_CHECKING, Optional, Dict from typing import TYPE_CHECKING, Optional, Dict
from cura.Machines.Models.IntentTranslations import intent_translations
from cura.Machines.Models.IntentModel import IntentModel from cura.Machines.Models.IntentModel import IntentModel
from cura.Settings.IntentManager import IntentManager from cura.Settings.IntentManager import IntentManager
@ -29,21 +30,29 @@ class IntentCategoryModel(ListModel):
modelUpdated = pyqtSignal() modelUpdated = pyqtSignal()
_translations = collections.OrderedDict() # type: "collections.OrderedDict[str,Dict[str,Optional[str]]]"
# Translations to user-visible string. Ordered by weight. # Translations to user-visible string. Ordered by weight.
# TODO: Create a solution for this name and weight to be used dynamically. # TODO: Create a solution for this name and weight to be used dynamically.
_translations = collections.OrderedDict() # type: "collections.OrderedDict[str,Dict[str,Optional[str]]]" @classmethod
_translations["default"] = { def _get_translations(cls):
"name": catalog.i18nc("@label", "Default") if len(cls._translations) == 0:
} cls._translations["default"] = {
_translations["engineering"] = { "name": catalog.i18nc("@label", "Default")
"name": catalog.i18nc("@label", "Engineering"), }
"description": catalog.i18nc("@text", "Suitable for engineering work") cls._translations["visual"] = {
"name": catalog.i18nc("@label", "Visual"),
} "description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality.")
_translations["smooth"] = { }
"name": catalog.i18nc("@label", "Smooth"), cls._translations["engineering"] = {
"description": catalog.i18nc("@text", "Optimized for a smooth surfaces") "name": catalog.i18nc("@label", "Engineering"),
} "description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances.")
}
cls._translations["quick"] = {
"name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction.")
}
return cls._translations
## Creates a new model for a certain intent category. ## Creates a new model for a certain intent category.
# \param The category to list the intent profiles for. # \param The category to list the intent profiles for.
@ -95,15 +104,15 @@ class IntentCategoryModel(ListModel):
"name": IntentCategoryModel.translation(category, "name", catalog.i18nc("@label", "Unknown")), "name": IntentCategoryModel.translation(category, "name", catalog.i18nc("@label", "Unknown")),
"description": IntentCategoryModel.translation(category, "description", None), "description": IntentCategoryModel.translation(category, "description", None),
"intent_category": category, "intent_category": category,
"weight": list(self._translations.keys()).index(category), "weight": list(IntentCategoryModel._get_translations().keys()).index(category),
"qualities": qualities "qualities": qualities
}) })
result.sort(key = lambda k: k["weight"]) result.sort(key = lambda k: k["weight"])
self.setItems(result) self.setItems(result)
## Get a display value for a category. See IntenCategoryModel._translations ## Get a display value for a category.
## for categories and keys ## for categories and keys
@staticmethod @staticmethod
def translation(category: str, key: str, default: Optional[str] = None): def translation(category: str, key: str, default: Optional[str] = None):
display_strings = IntentCategoryModel._translations.get(category, {}) display_strings = IntentCategoryModel._get_translations().get(category, {})
return display_strings.get(key, default) return display_strings.get(key, default)

View file

@ -0,0 +1,24 @@
import collections
from typing import Dict, Optional
from UM.i18n import i18nCatalog
from typing import Dict, Optional
catalog = i18nCatalog("cura")
intent_translations = collections.OrderedDict() # type: collections.OrderedDict[str, Dict[str, Optional[str]]]
intent_translations["default"] = {
"name": catalog.i18nc("@label", "Default")
}
intent_translations["visual"] = {
"name": catalog.i18nc("@label", "Visual"),
"description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality.")
}
intent_translations["engineering"] = {
"name": catalog.i18nc("@label", "Engineering"),
"description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances.")
}
intent_translations["quick"] = {
"name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction.")
}

View file

@ -14,6 +14,7 @@ from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_quality_changes_container from cura.Settings.cura_empty_instance_containers import empty_quality_changes_container
from cura.Settings.IntentManager import IntentManager from cura.Settings.IntentManager import IntentManager
from cura.Machines.Models.MachineModelUtils import fetchLayerHeight from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
from cura.Machines.Models.IntentTranslations import intent_translations
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -336,10 +337,11 @@ class QualityManagementModel(ListModel):
"quality_type": quality_type, "quality_type": quality_type,
"quality_changes_group": None, "quality_changes_group": None,
"intent_category": intent_category, "intent_category": intent_category,
"section_name": catalog.i18nc("@label", intent_category.capitalize()), "section_name": catalog.i18nc("@label", intent_translations.get(intent_category, {}).get("name", catalog.i18nc("@label", "Unknown"))),
}) })
# Sort by quality_type for each intent category # Sort by quality_type for each intent category
result = sorted(result, key = lambda x: (x["intent_category"], x["quality_type"]))
result = sorted(result, key = lambda x: (list(intent_translations).index(x["intent_category"]), x["quality_type"]))
item_list += result item_list += result
# Create quality_changes group items # Create quality_changes group items

View file

@ -1,13 +1,12 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional, TYPE_CHECKING from typing import TYPE_CHECKING
from UM.Logger import Logger from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface from UM.Settings.Interfaces import ContainerInterface
from UM.Signal import Signal from UM.Signal import Signal
from cura.Settings.cura_empty_instance_containers import empty_variant_container
from cura.Machines.ContainerNode import ContainerNode from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.MaterialNode import MaterialNode from cura.Machines.MaterialNode import MaterialNode
@ -83,12 +82,22 @@ class VariantNode(ContainerNode):
# if there is no match. # if there is no match.
def preferredMaterial(self, approximate_diameter: int) -> MaterialNode: def preferredMaterial(self, approximate_diameter: int) -> MaterialNode:
for base_material, material_node in self.materials.items(): for base_material, material_node in self.materials.items():
if self.machine.preferred_material in base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")): if self.machine.preferred_material == base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
return material_node return material_node
# First fallback: Choose any material with matching diameter.
# First fallback: Check if we should be checking for the 175 variant.
if approximate_diameter == 2:
preferred_material = self.machine.preferred_material + "_175"
for base_material, material_node in self.materials.items():
if preferred_material == base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
return material_node
# Second fallback: Choose any material with matching diameter.
for material_node in self.materials.values(): for material_node in self.materials.values():
if material_node.getMetaDataEntry("approximate_diameter") and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")): if material_node.getMetaDataEntry("approximate_diameter") and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
Logger.log("w", "Could not find preferred material %s, falling back to whatever works", self.machine.preferred_material)
return material_node return material_node
fallback = next(iter(self.materials.values())) # Should only happen with empty material node. fallback = next(iter(self.materials.values())) # Should only happen with empty material node.
Logger.log("w", "Could not find preferred material {preferred_material} with diameter {diameter} for variant {variant_id}, falling back to {fallback}.".format( Logger.log("w", "Could not find preferred material {preferred_material} with diameter {diameter} for variant {variant_id}, falling back to {fallback}.".format(
preferred_material = self.machine.preferred_material, preferred_material = self.machine.preferred_material,

View file

@ -99,7 +99,7 @@ class AuthorizationHelpers:
}) })
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
# Connection was suddenly dropped. Nothing we can do about that. # Connection was suddenly dropped. Nothing we can do about that.
Logger.log("w", "Something failed while attempting to parse the JWT token") Logger.logException("w", "Something failed while attempting to parse the JWT token")
return None return None
if token_request.status_code not in (200, 201): if token_request.status_code not in (200, 201):
Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text) Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text)

View file

@ -3,7 +3,6 @@
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from urllib.parse import urlencode from urllib.parse import urlencode
@ -14,7 +13,6 @@ from PyQt5.QtGui import QDesktopServices
from UM.Logger import Logger from UM.Logger import Logger
from UM.Message import Message from UM.Message import Message
from UM.Platform import Platform
from UM.Signal import Signal from UM.Signal import Signal
from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer

View file

@ -17,6 +17,7 @@ from cura.Scene import ZOffsetDecorator
import random # used for list shuffling import random # used for list shuffling
class PlatformPhysics: class PlatformPhysics:
def __init__(self, controller, volume): def __init__(self, controller, volume):
super().__init__() super().__init__()

View file

@ -20,7 +20,7 @@ class FirmwareUpdater(QObject):
self._output_device = output_device self._output_device = output_device
self._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True) self._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True, name = "FirmwareUpdateThread")
self._firmware_file = "" self._firmware_file = ""
self._firmware_progress = 0 self._firmware_progress = 0
@ -43,7 +43,7 @@ class FirmwareUpdater(QObject):
## Cleanup after a succesful update ## Cleanup after a succesful update
def _cleanupAfterUpdate(self) -> None: def _cleanupAfterUpdate(self) -> None:
# Clean up for next attempt. # Clean up for next attempt.
self._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True) self._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True, name = "FirmwareUpdateThread")
self._firmware_file = "" self._firmware_file = ""
self._onFirmwareProgress(100) self._onFirmwareProgress(100)
self._setFirmwareUpdateState(FirmwareUpdateState.completed) self._setFirmwareUpdateState(FirmwareUpdateState.completed)

View file

@ -4,12 +4,12 @@ from cura.Scene.CuraSceneNode import CuraSceneNode
## Make a SceneNode build plate aware CuraSceneNode objects all have this decorator. ## Make a SceneNode build plate aware CuraSceneNode objects all have this decorator.
class BuildPlateDecorator(SceneNodeDecorator): class BuildPlateDecorator(SceneNodeDecorator):
def __init__(self, build_plate_number = -1): def __init__(self, build_plate_number: int = -1) -> None:
super().__init__() super().__init__()
self._build_plate_number = None self._build_plate_number = build_plate_number
self.setBuildPlateNumber(build_plate_number) self.setBuildPlateNumber(build_plate_number)
def setBuildPlateNumber(self, nr): def setBuildPlateNumber(self, nr: int) -> None:
# Make sure that groups are set correctly # Make sure that groups are set correctly
# setBuildPlateForSelection in CuraActions makes sure that no single childs are set. # setBuildPlateForSelection in CuraActions makes sure that no single childs are set.
self._build_plate_number = nr self._build_plate_number = nr
@ -19,7 +19,7 @@ class BuildPlateDecorator(SceneNodeDecorator):
for child in self._node.getChildren(): for child in self._node.getChildren():
child.callDecoration("setBuildPlateNumber", nr) child.callDecoration("setBuildPlateNumber", nr)
def getBuildPlateNumber(self): def getBuildPlateNumber(self) -> int:
return self._build_plate_number return self._build_plate_number
def __deepcopy__(self, memo): def __deepcopy__(self, memo):

View file

@ -1,6 +1,6 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional from typing import Optional, TYPE_CHECKING
from UM.Application import Application from UM.Application import Application
from UM.Math.Polygon import Polygon from UM.Math.Polygon import Polygon
@ -11,6 +11,9 @@ from UM.Math.Color import Color
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with. from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
if TYPE_CHECKING:
from UM.Mesh.MeshData import MeshData
class ConvexHullNode(SceneNode): class ConvexHullNode(SceneNode):
shader = None # To prevent the shader from being re-built over and over again, only load it once. shader = None # To prevent the shader from being re-built over and over again, only load it once.
@ -44,7 +47,7 @@ class ConvexHullNode(SceneNode):
# The node this mesh is "watching" # The node this mesh is "watching"
self._node = node self._node = node
# Area of the head + fans for display as a shadow on the buildplate # Area of the head + fans for display as a shadow on the buildplate
self._convex_hull_head_mesh = None self._convex_hull_head_mesh = None # type: Optional[MeshData]
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
self._onNodeDecoratorsChanged(self._node) self._onNodeDecoratorsChanged(self._node)

View file

@ -247,7 +247,7 @@ class ContainerManager(QObject):
try: try:
with open(file_url, "rt", encoding = "utf-8") as f: with open(file_url, "rt", encoding = "utf-8") as f:
container.deserialize(f.read()) container.deserialize(f.read(), file_url)
except PermissionError: except PermissionError:
return {"status": "error", "message": "Permission denied when trying to read the file."} return {"status": "error", "message": "Permission denied when trying to read the file."}
except ContainerFormatError: except ContainerFormatError:
@ -339,11 +339,11 @@ class ContainerManager(QObject):
# \return A list of names of materials with the same GUID. # \return A list of names of materials with the same GUID.
@pyqtSlot("QVariant", bool, result = "QStringList") @pyqtSlot("QVariant", bool, result = "QStringList")
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]: def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]:
same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(guid = material_node.guid) same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(GUID = material_node.guid)
if exclude_self: if exclude_self:
return [metadata["name"] for metadata in same_guid if metadata["base_file"] != material_node.base_file] return list({meta["name"] for meta in same_guid if meta["base_file"] != material_node.base_file})
else: else:
return [metadata["name"] for metadata in same_guid] return list({meta["name"] for meta in same_guid})
## Unlink a material from all other materials by creating a new GUID ## Unlink a material from all other materials by creating a new GUID
# \param material_id \type{str} the id of the material to create a new GUID for. # \param material_id \type{str} the id of the material to create a new GUID for.

View file

@ -12,7 +12,6 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID. from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
from UM.Decorators import deprecated
from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING, Union from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING, Union
@ -369,7 +368,7 @@ class ExtruderManager(QObject):
printer = global_stack.getId(), expected = expected_extruder_definition_0_id, got = extruder_stack_0.definition.getId())) printer = global_stack.getId(), expected = expected_extruder_definition_0_id, got = extruder_stack_0.definition.getId()))
try: try:
extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0] extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0]
except IndexError as e: except IndexError:
# It still needs to break, but we want to know what extruder ID made it break. # It still needs to break, but we want to know what extruder ID made it break.
msg = "Unable to find extruder definition with the id [%s]" % expected_extruder_definition_0_id msg = "Unable to find extruder definition with the id [%s]" % expected_extruder_definition_0_id
Logger.logException("e", msg) Logger.logException("e", msg)

View file

@ -1,13 +1,15 @@
#Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
import cura.CuraApplication
from UM.Logger import Logger from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer
import cura.CuraApplication
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container from cura.Settings.cura_empty_instance_containers import empty_intent_container
from UM.Settings.InstanceContainer import InstanceContainer
if TYPE_CHECKING: if TYPE_CHECKING:
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
@ -36,8 +38,12 @@ class IntentManager(QObject):
# \return A list of metadata dictionaries matching the search criteria, or # \return A list of metadata dictionaries matching the search criteria, or
# an empty list if nothing was found. # an empty list if nothing was found.
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]: def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
material_node = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials[material_base_file] intent_metadatas = [] # type: List[Dict[str, Any]]
intent_metadatas = [] materials = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials
if material_base_file not in materials:
return intent_metadatas
material_node = materials[material_base_file]
for quality_node in material_node.qualities.values(): for quality_node in material_node.qualities.values():
for intent_node in quality_node.intents.values(): for intent_node in quality_node.intents.values():
intent_metadatas.append(intent_node.getMetadata()) intent_metadatas.append(intent_node.getMetadata())
@ -116,7 +122,7 @@ class IntentManager(QObject):
## The intent that gets selected by default when no intent is available for ## The intent that gets selected by default when no intent is available for
# the configuration, an extruder can't match the intent that the user # the configuration, an extruder can't match the intent that the user
# selects, or just when creating a new printer. # selects, or just when creating a new printer.
def getDefaultIntent(self) -> InstanceContainer: def getDefaultIntent(self) -> "InstanceContainer":
return empty_intent_container return empty_intent_container
@pyqtProperty(str, notify = intentCategoryChanged) @pyqtProperty(str, notify = intentCategoryChanged)

View file

@ -28,20 +28,21 @@ if TYPE_CHECKING:
class SettingInheritanceManager(QObject): class SettingInheritanceManager(QObject):
def __init__(self, parent = None) -> None: def __init__(self, parent = None) -> None:
super().__init__(parent) super().__init__(parent)
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._global_container_stack = None # type: Optional[ContainerStack] self._global_container_stack = None # type: Optional[ContainerStack]
self._settings_with_inheritance_warning = [] # type: List[str] self._settings_with_inheritance_warning = [] # type: List[str]
self._active_container_stack = None # type: Optional[ExtruderStack] self._active_container_stack = None # type: Optional[ExtruderStack]
self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
self._onActiveExtruderChanged()
self._update_timer = QTimer() self._update_timer = QTimer()
self._update_timer.setInterval(500) self._update_timer.setInterval(500)
self._update_timer.setSingleShot(True) self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._update) self._update_timer.timeout.connect(self._update)
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
self._onGlobalContainerChanged()
self._onActiveExtruderChanged()
settingsWithIntheritanceChanged = pyqtSignal() settingsWithIntheritanceChanged = pyqtSignal()
## Get the keys of all children settings with an override. ## Get the keys of all children settings with an override.
@ -106,7 +107,7 @@ class SettingInheritanceManager(QObject):
if self._active_container_stack is not None: if self._active_container_stack is not None:
self._active_container_stack.propertyChanged.connect(self._onPropertyChanged) self._active_container_stack.propertyChanged.connect(self._onPropertyChanged)
self._active_container_stack.containersChanged.connect(self._onContainersChanged) self._active_container_stack.containersChanged.connect(self._onContainersChanged)
self._update() # Ensure that the settings_with_inheritance_warning list is populated. self._update_timer.start() # Ensure that the settings_with_inheritance_warning list is populated.
def _onPropertyChanged(self, key: str, property_name: str) -> None: def _onPropertyChanged(self, key: str, property_name: str) -> None:
if (property_name == "value" or property_name == "enabled") and self._global_container_stack: if (property_name == "value" or property_name == "enabled") and self._global_container_stack:

View file

@ -56,11 +56,11 @@ class CuraSplashScreen(QSplashScreen):
if buildtype: if buildtype:
version[0] += " (%s)" % buildtype version[0] += " (%s)" % buildtype
# draw version text # Draw version text
font = QFont() # Using system-default font here font = QFont() # Using system-default font here
font.setPixelSize(37) font.setPixelSize(37)
painter.setFont(font) painter.setFont(font)
painter.drawText(215, 66, 330 * self._scale, 230 * self._scale, Qt.AlignLeft | Qt.AlignTop, version[0]) painter.drawText(60, 66, 330 * self._scale, 230 * self._scale, Qt.AlignLeft | Qt.AlignTop, version[0])
if len(version) > 1: if len(version) > 1:
font.setPixelSize(16) font.setPixelSize(16)
painter.setFont(font) painter.setFont(font)
@ -68,14 +68,14 @@ class CuraSplashScreen(QSplashScreen):
painter.drawText(247, 105, 330 * self._scale, 255 * self._scale, Qt.AlignLeft | Qt.AlignTop, version[1]) painter.drawText(247, 105, 330 * self._scale, 255 * self._scale, Qt.AlignLeft | Qt.AlignTop, version[1])
painter.setPen(QColor(255, 255, 255, 255)) painter.setPen(QColor(255, 255, 255, 255))
# draw the loading image # Draw the loading image
pen = QPen() pen = QPen()
pen.setWidth(6 * self._scale) pen.setWidth(6 * self._scale)
pen.setColor(QColor(32, 166, 219, 255)) pen.setColor(QColor(32, 166, 219, 255))
painter.setPen(pen) painter.setPen(pen)
painter.drawArc(60, 150, 32 * self._scale, 32 * self._scale, self._loading_image_rotation_angle * 16, 300 * 16) painter.drawArc(60, 150, 32 * self._scale, 32 * self._scale, self._loading_image_rotation_angle * 16, 300 * 16)
# draw message text # Draw message text
if self._current_message: if self._current_message:
font = QFont() # Using system-default font here font = QFont() # Using system-default font here
font.setPixelSize(13) font.setPixelSize(13)

View file

@ -13,8 +13,8 @@ UM.Dialog
id: base id: base
title: catalog.i18nc("@title:window", "Open Project") title: catalog.i18nc("@title:window", "Open Project")
minimumWidth: 500 * screenScaleFactor minimumWidth: UM.Theme.getSize("popup_dialog").width
minimumHeight: 450 * screenScaleFactor minimumHeight: UM.Theme.getSize("popup_dialog").height
width: minimumWidth width: minimumWidth
height: minimumHeight height: minimumHeight

View file

@ -12,7 +12,6 @@ except ImportError:
from . import ThreeMFWorkspaceReader from . import ThreeMFWorkspaceReader
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Platform import Platform
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")

View file

@ -18,6 +18,7 @@ Window
minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height) minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height)
maximumWidth: Math.round(minimumWidth * 1.2) maximumWidth: Math.round(minimumWidth * 1.2)
maximumHeight: Math.round(minimumHeight * 1.2) maximumHeight: Math.round(minimumHeight * 1.2)
modality: Qt.ApplicationModal
width: minimumWidth width: minimumWidth
height: minimumHeight height: minimumHeight
color: UM.Theme.getColor("main_background") color: UM.Theme.getColor("main_background")

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import numpy import numpy
@ -72,7 +72,7 @@ class GcodeStartEndFormatter(Formatter):
value = default_value_str value = default_value_str
# "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value. # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value.
if key in kwargs["-1"]: if key in kwargs["-1"]:
value = kwargs["-1"] value = kwargs["-1"][key]
if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]: if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]:
value = kwargs[str(extruder_nr)][key] value = kwargs[str(extruder_nr)][key]

View file

@ -143,6 +143,52 @@ UM.Dialog
} }
} }
UM.TooltipArea {
Layout.fillWidth:true
height: childrenRect.height
text: catalog.i18nc("@info:tooltip","For lithophanes a simple logarithmic model for translucency is available. For height maps the pixel values correspond to heights linearly.")
Row {
width: parent.width
Label {
text: "Color Model"
width: 150 * screenScaleFactor
anchors.verticalCenter: parent.verticalCenter
}
ComboBox {
id: color_model
objectName: "ColorModel"
model: [ catalog.i18nc("@item:inlistbox","Linear"), catalog.i18nc("@item:inlistbox","Translucency") ]
width: 180 * screenScaleFactor
onCurrentIndexChanged: { manager.onColorModelChanged(currentIndex) }
}
}
}
UM.TooltipArea {
Layout.fillWidth:true
height: childrenRect.height
text: catalog.i18nc("@info:tooltip","The percentage of light penetrating a print with a thickness of 1 millimeter. Lowering this value increases the contrast in dark regions and decreases the contrast in light regions of the image.")
visible: color_model.currentText == catalog.i18nc("@item:inlistbox","Translucency")
Row {
width: parent.width
Label {
text: catalog.i18nc("@action:label", "1mm Transmittance (%)")
width: 150 * screenScaleFactor
anchors.verticalCenter: parent.verticalCenter
}
TextField {
id: transmittance
objectName: "Transmittance"
focus: true
validator: RegExpValidator {regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onTransmittanceChanged(text) }
}
}
}
UM.TooltipArea { UM.TooltipArea {
Layout.fillWidth:true Layout.fillWidth:true
height: childrenRect.height height: childrenRect.height

View file

@ -3,6 +3,8 @@
import numpy import numpy
import math
from PyQt5.QtGui import QImage, qRed, qGreen, qBlue from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
@ -46,9 +48,9 @@ class ImageReader(MeshReader):
def _read(self, file_name): def _read(self, file_name):
size = max(self._ui.getWidth(), self._ui.getDepth()) size = max(self._ui.getWidth(), self._ui.getDepth())
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher) return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher, self._ui.use_transparency_model, self._ui.transmittance_1mm)
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher): def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm):
scene_node = SceneNode() scene_node = SceneNode()
mesh = MeshBuilder() mesh = MeshBuilder()
@ -99,12 +101,14 @@ class ImageReader(MeshReader):
for x in range(0, width): for x in range(0, width):
for y in range(0, height): for y in range(0, height):
qrgb = img.pixel(x, y) qrgb = img.pixel(x, y)
avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255) if use_transparency_model:
height_data[y, x] = avg height_data[y, x] = (0.299 * math.pow(qRed(qrgb) / 255.0, 2.2) + 0.587 * math.pow(qGreen(qrgb) / 255.0, 2.2) + 0.114 * math.pow(qBlue(qrgb) / 255.0, 2.2))
else:
height_data[y, x] = (0.212655 * qRed(qrgb) + 0.715158 * qGreen(qrgb) + 0.072187 * qBlue(qrgb)) / 255 # fast computation ignoring gamma and degamma
Job.yieldThread() Job.yieldThread()
if not lighter_is_higher: if lighter_is_higher == use_transparency_model:
height_data = 1 - height_data height_data = 1 - height_data
for _ in range(0, blur_iterations): for _ in range(0, blur_iterations):
@ -124,8 +128,15 @@ class ImageReader(MeshReader):
Job.yieldThread() Job.yieldThread()
height_data *= scale_vector.y if use_transparency_model:
height_data += base_height divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel.
min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height)
for (y, x) in numpy.ndindex(height_data.shape):
mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x]
height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this
else:
height_data *= scale_vector.y
height_data += base_height
heightmap_face_count = 2 * height_minus_one * width_minus_one heightmap_face_count = 2 * height_minus_one * width_minus_one
total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2 total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2

View file

@ -34,6 +34,8 @@ class ImageReaderUI(QObject):
self.peak_height = 2.5 self.peak_height = 2.5
self.smoothing = 1 self.smoothing = 1
self.lighter_is_higher = False; self.lighter_is_higher = False;
self.use_transparency_model = True;
self.transmittance_1mm = 50.0; # based on pearl PLA
self._ui_lock = threading.Lock() self._ui_lock = threading.Lock()
self._cancelled = False self._cancelled = False
@ -75,6 +77,7 @@ class ImageReaderUI(QObject):
self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height)) self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height)) self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
self._ui_view.findChild(QObject, "Transmittance").setProperty("text", str(self.transmittance_1mm))
self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing) self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)
def _createConfigUI(self): def _createConfigUI(self):
@ -144,3 +147,11 @@ class ImageReaderUI(QObject):
@pyqtSlot(int) @pyqtSlot(int)
def onImageColorInvertChanged(self, value): def onImageColorInvertChanged(self, value):
self.lighter_is_higher = (value == 1) self.lighter_is_higher = (value == 1)
@pyqtSlot(int)
def onColorModelChanged(self, value):
self.use_transparency_model = (value == 0)
@pyqtSlot(int)
def onTransmittanceChanged(self, value):
self.transmittance_1mm = value

View file

@ -157,7 +157,7 @@ class LegacyProfileReader(ProfileReader):
data = stream.getvalue() data = stream.getvalue()
profile = InstanceContainer(profile_id) profile = InstanceContainer(profile_id)
profile.deserialize(data) # Also performs the version upgrade. profile.deserialize(data, file_name) # Also performs the version upgrade.
profile.setDirty(True) profile.setDirty(True)
#We need to return one extruder stack and one global stack. #We need to return one extruder stack and one global stack.

View file

@ -5,7 +5,6 @@ import configparser # An input for some functions we're testing.
import os.path # To find the integration test .ini files. import os.path # To find the integration test .ini files.
import pytest # To register tests with. import pytest # To register tests with.
import unittest.mock # To mock the application, plug-in and container registry out. import unittest.mock # To mock the application, plug-in and container registry out.
import os.path
import sys import sys
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
@ -16,6 +15,7 @@ import UM.Settings.InstanceContainer # To intercept the serialised data from the
import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module. import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module.
@pytest.fixture @pytest.fixture
def legacy_profile_reader(): def legacy_profile_reader():
try: try:
@ -162,7 +162,7 @@ def test_read(legacy_profile_reader, file_name):
plugin_registry.getPluginPath = unittest.mock.MagicMock(return_value = os.path.dirname(LegacyProfileReaderModule.__file__)) plugin_registry.getPluginPath = unittest.mock.MagicMock(return_value = os.path.dirname(LegacyProfileReaderModule.__file__))
# Mock out the resulting InstanceContainer so that we can intercept the data before it's passed through the version upgrader. # Mock out the resulting InstanceContainer so that we can intercept the data before it's passed through the version upgrader.
def deserialize(self, data): # Intercepts the serialised data that we'd perform the version upgrade from when deserializing. def deserialize(self, data, filename): # Intercepts the serialised data that we'd perform the version upgrade from when deserializing.
global intercepted_data global intercepted_data
intercepted_data = data intercepted_data = data
@ -192,4 +192,4 @@ def test_read(legacy_profile_reader, file_name):
assert parser["metadata"]["type"] == "quality_changes" assert parser["metadata"]["type"] == "quality_changes"
assert parser["metadata"]["quality_type"] == "normal" assert parser["metadata"]["quality_type"] == "normal"
assert parser["metadata"]["position"] == "0" assert parser["metadata"]["position"] == "0"
assert parser["metadata"]["setting_version"] == "5" # Yes, before we upgraded. assert parser["metadata"]["setting_version"] == "5" # Yes, before we upgraded.

View file

@ -87,9 +87,25 @@ Cura.MachineAction
} }
} }
} }
Label
{
id: machineNameLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
text: Cura.MachineManager.activeMachine.name
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
}
UM.TabRow UM.TabRow
{ {
id: tabBar id: tabBar
anchors.top: machineNameLabel.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width width: parent.width
Repeater Repeater
{ {

View file

@ -331,6 +331,18 @@ Item
onGlobalContainerChanged: extruderCountModel.update() onGlobalContainerChanged: extruderCountModel.update()
} }
} }
Cura.SimpleCheckBox // "Shared Heater"
{
id: sharedHeaterCheckBox
containerStackId: machineStackId
settingKey: "machine_extruders_share_heater"
settingStoreIndex: propertyStoreIndex
labelText: catalog.i18nc("@label", "Shared Heater")
labelFont: base.labelFont
labelWidth: base.labelWidth
forceUpdateOnChangeFunction: forceUpdateFunction
}
} }
} }

View file

@ -1,7 +1,7 @@
# Copyright (c) 2016 Ultimaker B.V. # Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal from PyQt5.QtCore import pyqtProperty
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from UM.Application import Application from UM.Application import Application
@ -13,6 +13,7 @@ import UM.Settings.Models.SettingVisibilityHandler
from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherits-stack setting values from different extruders. from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherits-stack setting values from different extruders.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
## The per object setting visibility handler ensures that only setting ## The per object setting visibility handler ensures that only setting
# definitions that have a matching instance Container are returned as visible. # definitions that have a matching instance Container are returned as visible.
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler): class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler):

View file

@ -12,14 +12,18 @@ import Cura 1.0 as Cura
Item Item
{ {
// An Item whose bounds are guaranteed to be safe for overlays to be placed.
// Defaults to parent, ie. the entire available area
property var safeArea: parent
// Subtract the actionPanel from the safe area. This way the view won't draw interface elements under/over it // Subtract the actionPanel from the safe area. This way the view won't draw interface elements under/over it
Item { Item
id: safeArea {
visible: false id: childSafeArea
anchors.left: parent.left x: safeArea.x - parent.x
anchors.right: actionPanelWidget.left y: safeArea.y - parent.y
anchors.top: parent.top width: actionPanelWidget.x - x
anchors.bottom: actionPanelWidget.top height: actionPanelWidget.y - y
} }
Loader Loader
@ -29,9 +33,10 @@ Item
source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : "" source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : ""
onLoaded: { onLoaded:
{
if (previewMain.item.safeArea !== undefined){ if (previewMain.item.safeArea !== undefined){
previewMain.item.safeArea = Qt.binding(function() { return safeArea }); previewMain.item.safeArea = Qt.binding(function() { return childSafeArea });
} }
} }
} }

View file

@ -48,9 +48,13 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
drives = {} drives = {}
bitmask = ctypes.windll.kernel32.GetLogicalDrives() bitmask = ctypes.windll.kernel32.GetLogicalDrives()
# Check possible drive letters, from A to Z # Check possible drive letters, from C to Z
# Note: using ascii_uppercase because we do not want this to change with locale! # Note: using ascii_uppercase because we do not want this to change with locale!
for letter in string.ascii_uppercase: # Skip A and B, since those drives are typically reserved for floppy disks.
# Those drives can theoretically be reassigned but it's safer to not check them for removable drives.
# Windows will also behave weirdly even with some of its internal functions if you do this (e.g. search indexing doesn't search it).
# Users that have removable drives in A or B will just have to save to file and select the drive there.
for letter in string.ascii_uppercase[2:]:
drive = "{0}:/".format(letter) drive = "{0}:/".format(letter)
# Do we really want to skip A and B? # Do we really want to skip A and B?

View file

@ -155,30 +155,19 @@ Item
} }
onPositionChanged: parent.onHandleDragged() onPositionChanged: parent.onHandleDragged()
onPressed: sliderRoot.setActiveHandle(rangeHandle) onPressed:
{
sliderRoot.setActiveHandle(rangeHandle)
sliderRoot.forceActiveFocus()
}
} }
SimulationSliderLabel
{
id: rangleHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
x: parent.x - width - UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.verticalCenter
target: Qt.point(sliderRoot.width, y + height / 2)
visible: sliderRoot.activeHandle == parent
// custom properties
maximumValue: sliderRoot.maximumValue
value: sliderRoot.upperValue
busy: UM.SimulationView.busy
setValue: rangeHandle.setValueManually // connect callback functions
}
} }
onHeightChanged : { onHeightChanged : {
// After a height change, the pixel-position of the lower handle is out of sync with the property value // After a height change, the pixel-position of the handles is out of sync with the property value
setLowerValue(lowerValue) setLowerValue(lowerValue)
setUpperValue(upperValue)
} }
// Upper handle // Upper handle
@ -275,11 +264,12 @@ Item
{ {
id: upperHandleLabel id: upperHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height height: sliderRoot.handleSize
x: parent.x - parent.width - width anchors.bottom: parent.top
anchors.verticalCenter: parent.verticalCenter anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height
target: Qt.point(sliderRoot.width, y + height / 2) anchors.horizontalCenter: parent.horizontalCenter
visible: sliderRoot.activeHandle == parent target: Qt.point(parent.width / 2, parent.top)
visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle
// custom properties // custom properties
maximumValue: sliderRoot.maximumValue maximumValue: sliderRoot.maximumValue
@ -384,11 +374,12 @@ Item
{ {
id: lowerHandleLabel id: lowerHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height height: sliderRoot.handleSize
x: parent.x - parent.width - width anchors.top: parent.bottom
anchors.verticalCenter: parent.verticalCenter anchors.topMargin: UM.Theme.getSize("narrow_margin").height
target: Qt.point(sliderRoot.width + width, y + height / 2) anchors.horizontalCenter: parent.horizontalCenter
visible: sliderRoot.activeHandle == parent target: Qt.point(parent.width / 2, parent.bottom)
visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle
// custom properties // custom properties
maximumValue: sliderRoot.maximumValue maximumValue: sliderRoot.maximumValue
@ -397,4 +388,4 @@ Item
setValue: lowerHandle.setValueManually // connect callback functions setValue: lowerHandle.setValueManually // connect callback functions
} }
} }
} }

View file

@ -3,7 +3,6 @@
from UM.Application import Application from UM.Application import Application
from UM.Math.Color import Color from UM.Math.Color import Color
from UM.Math.Vector import Vector
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL

View file

@ -56,6 +56,11 @@ Item
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue) return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
} }
onWidthChanged : {
// After a width change, the pixel-position of the handle is out of sync with the property value
setHandleValue(handleValue)
}
// slider track // slider track
Rectangle Rectangle
{ {

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 Ultimaker B.V. // Copyright (c) 2017 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.5
import QtQuick 2.2
import QtQuick.Controls 1.2 import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
@ -20,9 +19,9 @@ UM.PointingRectangle {
property int startFrom: 1 property int startFrom: 1
target: Qt.point(parent.width, y + height / 2) target: Qt.point(parent.width, y + height / 2)
arrowSize: UM.Theme.getSize("default_arrow").width arrowSize: UM.Theme.getSize("button_tooltip_arrow").height
height: parent.height height: parent.height
width: valueLabel.width + UM.Theme.getSize("default_margin").width width: valueLabel.width
visible: false visible: false
color: UM.Theme.getColor("tool_panel_background") color: UM.Theme.getColor("tool_panel_background")
@ -40,26 +39,35 @@ UM.PointingRectangle {
anchors.fill: parent anchors.fill: parent
} }
TextMetrics {
id: maxValueMetrics
font: valueLabel.font
text: maximumValue + 1 // layers are 0 based, add 1 for display value
}
TextField { TextField {
id: valueLabel id: valueLabel
anchors { anchors {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
alignWhenCentered: false
} }
width: ((maximumValue + 1).toString().length + 1) * 10 * screenScaleFactor width: maxValueMetrics.width + UM.Theme.getSize("default_margin").width
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
horizontalAlignment: TextInput.AlignRight horizontalAlignment: TextInput.AlignHCenter
// key bindings, work when label is currenctly focused (active handle in LayerSlider) // key bindings, work when label is currenctly focused (active handle in LayerSlider)
Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
style: TextFieldStyle { style: TextFieldStyle {
textColor: UM.Theme.getColor("setting_control_text") textColor: UM.Theme.getColor("text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
background: Item { } renderType: Text.NativeRendering
background: Item { }
} }
onEditingFinished: { onEditingFinished: {

View file

@ -213,6 +213,8 @@ class SimulationView(CuraView):
def beginRendering(self) -> None: def beginRendering(self) -> None:
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() renderer = self.getRenderer()
if renderer is None:
return
if not self._ghost_shader: if not self._ghost_shader:
self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader")) self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
@ -490,7 +492,11 @@ class SimulationView(CuraView):
# Make sure the SimulationPass is created # Make sure the SimulationPass is created
layer_pass = self.getSimulationPass() layer_pass = self.getSimulationPass()
self.getRenderer().addRenderPass(layer_pass) renderer = self.getRenderer()
if renderer is None:
return False
renderer.addRenderPass(layer_pass)
# Make sure the NozzleNode is add to the root # Make sure the NozzleNode is add to the root
nozzle = self.getNozzleNode() nozzle = self.getNozzleNode()
@ -509,7 +515,7 @@ class SimulationView(CuraView):
self._simulationview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) self._simulationview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
if not self._composite_pass: if not self._composite_pass:
self._composite_pass = cast(CompositePass, self.getRenderer().getRenderPass("composite")) self._composite_pass = cast(CompositePass, renderer.getRenderPass("composite"))
self._old_layer_bindings = self._composite_pass.getLayerBindings()[:] # make a copy so we can restore to it later self._old_layer_bindings = self._composite_pass.getLayerBindings()[:] # make a copy so we can restore to it later
self._composite_pass.getLayerBindings().append("simulationview") self._composite_pass.getLayerBindings().append("simulationview")
@ -525,7 +531,13 @@ class SimulationView(CuraView):
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged) self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
if self._nozzle_node: if self._nozzle_node:
self._nozzle_node.setParent(None) self._nozzle_node.setParent(None)
self.getRenderer().removeRenderPass(self._layer_pass)
renderer = self.getRenderer()
if renderer is None:
return False
if self._layer_pass is not None:
renderer.removeRenderPass(self._layer_pass)
if self._composite_pass: if self._composite_pass:
self._composite_pass.setLayerBindings(cast(List[str], self._old_layer_bindings)) self._composite_pass.setLayerBindings(cast(List[str], self._old_layer_bindings))
self._composite_pass.setCompositeShader(cast(ShaderProgram, self._old_composite_shader)) self._composite_pass.setCompositeShader(cast(ShaderProgram, self._old_composite_shader))

View file

@ -18,7 +18,10 @@ Item
property bool isSimulationPlaying: false property bool isSimulationPlaying: false
readonly property var layerSliderSafeYMax: safeArea.y + safeArea.height readonly property real layerSliderSafeYMin: safeArea.y
readonly property real layerSliderSafeYMax: safeArea.y + safeArea.height
readonly property real pathSliderSafeXMin: safeArea.x + playButton.width
readonly property real pathSliderSafeXMax: safeArea.x + safeArea.width
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
@ -26,13 +29,21 @@ Item
PathSlider PathSlider
{ {
id: pathSlider id: pathSlider
readonly property real preferredWidth: UM.Theme.getSize("slider_layerview_size").height // not a typo, should be as long as layerview slider
readonly property real margin: UM.Theme.getSize("default_margin").width
readonly property real pathSliderSafeWidth: pathSliderSafeXMax - pathSliderSafeXMin
height: UM.Theme.getSize("slider_handle").width height: UM.Theme.getSize("slider_handle").width
width: UM.Theme.getSize("slider_layerview_size").height width: preferredWidth + margin * 2 < pathSliderSafeWidth ? preferredWidth : pathSliderSafeWidth - margin * 2
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: UM.Theme.getSize("default_margin").height anchors.bottomMargin: margin
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: -(parent.width - pathSliderSafeXMax - pathSliderSafeXMin) / 2 // center between parent top and layerSliderSafeYMax
visible: !UM.SimulationView.compatibilityMode visible: !UM.SimulationView.compatibilityMode
@ -183,17 +194,19 @@ Item
LayerSlider LayerSlider
{ {
property var preferredHeight: UM.Theme.getSize("slider_layerview_size").height property var preferredHeight: UM.Theme.getSize("slider_layerview_size").height
property double heightMargin: UM.Theme.getSize("default_margin").height property double heightMargin: UM.Theme.getSize("default_margin").height * 3 // extra margin to accomodate layer number tooltips
property double layerSliderSafeHeight: layerSliderSafeYMax - layerSliderSafeYMin
id: layerSlider id: layerSlider
width: UM.Theme.getSize("slider_handle").width width: UM.Theme.getSize("slider_handle").width
height: preferredHeight + heightMargin * 2 < layerSliderSafeYMax ? preferredHeight : layerSliderSafeYMax - heightMargin * 2 height: preferredHeight + heightMargin * 2 < layerSliderSafeHeight ? preferredHeight : layerSliderSafeHeight - heightMargin * 2
anchors anchors
{ {
right: parent.right right: parent.right
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
verticalCenterOffset: -(parent.height - layerSliderSafeYMax) / 2 // center between parent top and layerSliderSafeYMax verticalCenterOffset: -(parent.height - layerSliderSafeYMax - layerSliderSafeYMin) / 2 // center between parent top and layerSliderSafeYMax
rightMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width
bottomMargin: heightMargin bottomMargin: heightMargin
topMargin: heightMargin topMargin: heightMargin

View file

@ -1,11 +1,17 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
// Main window for the Toolbox
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import UM 1.1 as UM import UM 1.1 as UM
import "./pages"
import "./dialogs"
import "./components"
Window Window
{ {
id: base id: base
@ -14,8 +20,8 @@ Window
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
width: Math.floor(720 * screenScaleFactor) width: UM.Theme.getSize("large_popup_dialog").width
height: Math.floor(640 * screenScaleFactor) height: UM.Theme.getSize("large_popup_dialog").height
minimumWidth: width minimumWidth: width
maximumWidth: minimumWidth maximumWidth: minimumWidth
minimumHeight: height minimumHeight: height
@ -29,9 +35,16 @@ Window
Item Item
{ {
anchors.fill: parent anchors.fill: parent
WelcomePage
{
visible: toolbox.viewPage === "welcome"
}
ToolboxHeader ToolboxHeader
{ {
id: header id: header
visible: toolbox.viewPage !== "welcome"
} }
Item Item

View file

@ -67,7 +67,7 @@ Item
width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg" source: model.icon_url || "../../images/logobot.svg"
mipmap: true mipmap: true
} }
UM.RecolorImage UM.RecolorImage
@ -82,7 +82,7 @@ Item
sourceSize.height: height sourceSize.height: height
visible: installedPackages != 0 visible: installedPackages != 0
color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg" source: "../../images/installed_check.svg"
} }
} }
Item Item

View file

@ -23,7 +23,7 @@ Rectangle
height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg" source: model.icon_url || "../../images/logobot.svg"
mipmap: true mipmap: true
anchors anchors
{ {
@ -62,7 +62,7 @@ Rectangle
} }
visible: installedPackages != 0 visible: installedPackages != 0
color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg" source: "../../images/installed_check.svg"
} }
SmallRatingWidget SmallRatingWidget

View file

@ -14,7 +14,7 @@ import Cura 1.0 as Cura
UM.Dialog UM.Dialog
{ {
// This dialog asks the user whether he/she wants to open a project file as a project or import models. // This dialog asks the user to confirm he/she wants to uninstall materials/pprofiles which are currently in use
id: base id: base
title: catalog.i18nc("@title:window", "Confirm uninstall") + toolbox.pluginToUninstall title: catalog.i18nc("@title:window", "Confirm uninstall") + toolbox.pluginToUninstall

View file

@ -6,6 +6,8 @@ import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import "../components"
Item Item
{ {
id: page id: page
@ -31,7 +33,7 @@ Item
width: UM.Theme.getSize("toolbox_thumbnail_medium").width width: UM.Theme.getSize("toolbox_thumbnail_medium").width
height: UM.Theme.getSize("toolbox_thumbnail_medium").height height: UM.Theme.getSize("toolbox_thumbnail_medium").height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: details.icon_url || "../images/logobot.svg" source: details.icon_url || "../../images/logobot.svg"
mipmap: true mipmap: true
anchors anchors
{ {

View file

@ -8,6 +8,8 @@ import UM 1.1 as UM
import Cura 1.1 as Cura import Cura 1.1 as Cura
import "../components"
Item Item
{ {
id: page id: page
@ -44,7 +46,7 @@ Item
{ {
anchors.fill: parent anchors.fill: parent
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: details === null ? "" : (details.icon_url || "../images/logobot.svg") source: details === null ? "" : (details.icon_url || "../../images/logobot.svg")
mipmap: true mipmap: true
} }
} }

View file

@ -5,6 +5,8 @@ import QtQuick 2.10
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import UM 1.1 as UM import UM 1.1 as UM
import "../components"
ScrollView ScrollView
{ {
clip: true clip: true

View file

@ -6,6 +6,8 @@ import QtQuick.Controls 2.3
import UM 1.1 as UM import UM 1.1 as UM
import "../components"
ScrollView ScrollView
{ {
id: page id: page

View file

@ -0,0 +1,53 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import UM 1.3 as UM
import Cura 1.1 as Cura
Column
{
id: welcomePage
spacing: UM.Theme.getSize("wide_margin").height
width: parent.width
height: childrenRect.height
anchors.centerIn: parent
Image
{
id: profileImage
fillMode: Image.PreserveAspectFit
source: "../../images/logobot.svg"
anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(parent.width / 4)
}
Label
{
id: welcomeTextLabel
text: catalog.i18nc("@description", "Get plugins and materials verified by Ultimaker")
width: Math.round(parent.width / 2)
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Label.WordWrap
renderType: Text.NativeRendering
}
Cura.PrimaryButton
{
id: loginButton
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
anchors.horizontalCenter: parent.horizontalCenter
text: catalog.i18nc("@button", "Sign in")
onClicked: Cura.API.account.login()
fixedWidthMode: true
}
}

View file

@ -1,11 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import re
from typing import Dict
from PyQt5.QtCore import Qt, pyqtProperty from PyQt5.QtCore import Qt, pyqtProperty
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
## Model that holds supported configurations (for material/quality packages). ## Model that holds supported configurations (for material/quality packages).
class ConfigsModel(ListModel): class ConfigsModel(ListModel):
def __init__(self, parent = None): def __init__(self, parent = None):

View file

@ -48,7 +48,7 @@ class Toolbox(QObject, Extension):
self._download_progress = 0 # type: float self._download_progress = 0 # type: float
self._is_downloading = False # type: bool self._is_downloading = False # type: bool
self._network_manager = None # type: Optional[QNetworkAccessManager] self._network_manager = None # type: Optional[QNetworkAccessManager]
self._request_headers = [] # type: List[Tuple[bytes, bytes]] self._request_headers = [] # type: List[Tuple[bytes, bytes]]
self._updateRequestHeader() self._updateRequestHeader()
self._request_urls = {} # type: Dict[str, QUrl] self._request_urls = {} # type: Dict[str, QUrl]
@ -59,13 +59,15 @@ class Toolbox(QObject, Extension):
# The responses as given by the server parsed to a list. # The responses as given by the server parsed to a list.
self._server_response_data = { self._server_response_data = {
"authors": [], "authors": [],
"packages": [] "packages": [],
"updates": [],
} # type: Dict[str, List[Any]] } # type: Dict[str, List[Any]]
# Models: # Models:
self._models = { self._models = {
"authors": AuthorsModel(self), "authors": AuthorsModel(self),
"packages": PackagesModel(self), "packages": PackagesModel(self),
"updates": PackagesModel(self),
} # type: Dict[str, Union[AuthorsModel, PackagesModel]] } # type: Dict[str, Union[AuthorsModel, PackagesModel]]
self._plugins_showcase_model = PackagesModel(self) self._plugins_showcase_model = PackagesModel(self)
@ -86,7 +88,7 @@ class Toolbox(QObject, Extension):
# View page defines which type of page layout to use. For example, # View page defines which type of page layout to use. For example,
# possible values include "overview", "detail" or "author". # possible values include "overview", "detail" or "author".
self._view_page = "loading" # type: str self._view_page = "welcome" # type: str
# Active package refers to which package is currently being downloaded, # Active package refers to which package is currently being downloaded,
# installed, or otherwise modified. # installed, or otherwise modified.
@ -105,7 +107,6 @@ class Toolbox(QObject, Extension):
self._restart_dialog_message = "" # type: str self._restart_dialog_message = "" # type: str
self._application.initializationFinished.connect(self._onAppInitialized) self._application.initializationFinished.connect(self._onAppInitialized)
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
self._application.getCuraAPI().account.accessTokenChanged.connect(self._updateRequestHeader) self._application.getCuraAPI().account.accessTokenChanged.connect(self._updateRequestHeader)
# Signals: # Signals:
@ -126,6 +127,16 @@ class Toolbox(QObject, Extension):
showLicenseDialog = pyqtSignal() showLicenseDialog = pyqtSignal()
uninstallVariablesChanged = pyqtSignal() uninstallVariablesChanged = pyqtSignal()
## Go back to the start state (welcome screen or loading if no login required)
def _restart(self):
self._updateRequestHeader()
# For an Essentials build, login is mandatory
if not self._application.getCuraAPI().account.isLoggedIn and ApplicationMetadata.IsEnterpriseVersion:
self.setViewPage("welcome")
else:
self.setViewPage("loading")
self._fetchPackageData()
def _updateRequestHeader(self): def _updateRequestHeader(self):
self._request_headers = [ self._request_headers = [
(b"User-Agent", (b"User-Agent",
@ -186,18 +197,27 @@ class Toolbox(QObject, Extension):
cloud_api_version = self._cloud_api_version, cloud_api_version = self._cloud_api_version,
sdk_version = self._sdk_version sdk_version = self._sdk_version
) )
# We need to construct a query like installed_packages=ID:VERSION&installed_packages=ID:VERSION, etc.
installed_package_ids_with_versions = [":".join(items) for items in
self._package_manager.getAllInstalledPackageIdsAndVersions()]
installed_packages_query = "&installed_packages=".join(installed_package_ids_with_versions)
self._request_urls = { self._request_urls = {
"authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)),
"packages": QUrl("{base_url}/packages".format(base_url = self._api_url)) "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)),
"updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format(
base_url = self._api_url, query = installed_packages_query))
} }
# Request the latest and greatest! self._application.getCuraAPI().account.loginStateChanged.connect(self._restart)
self._fetchPackageData()
def _fetchPackageData(self): # On boot we check which packages have updates.
# Create the network manager: if CuraApplication.getInstance().getPreferences().getValue("info/automatic_update_check") and len(installed_package_ids_with_versions) > 0:
# This was formerly its own function but really had no reason to be as # Request the latest and greatest!
# it was never called more than once ever. self._fetchPackageUpdates()
def _prepareNetworkManager(self):
if self._network_manager is not None: if self._network_manager is not None:
self._network_manager.finished.disconnect(self._onRequestFinished) self._network_manager.finished.disconnect(self._onRequestFinished)
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged) self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged)
@ -205,16 +225,21 @@ class Toolbox(QObject, Extension):
self._network_manager.finished.connect(self._onRequestFinished) self._network_manager.finished.connect(self._onRequestFinished)
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccessibleChanged) self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccessibleChanged)
def _fetchPackageUpdates(self):
self._prepareNetworkManager()
self._makeRequestByType("updates")
def _fetchPackageData(self):
self._prepareNetworkManager()
# Make remote requests: # Make remote requests:
self._makeRequestByType("packages") self._makeRequestByType("packages")
self._makeRequestByType("authors") self._makeRequestByType("authors")
# Gather installed packages: # Gather installed packages:
self._updateInstalledModels() self._updateInstalledModels()
# Displays the toolbox
@pyqtSlot() @pyqtSlot()
def browsePackages(self) -> None: def launch(self) -> None:
self._fetchPackageData()
if not self._dialog: if not self._dialog:
self._dialog = self._createDialog("Toolbox.qml") self._dialog = self._createDialog("Toolbox.qml")
@ -223,6 +248,8 @@ class Toolbox(QObject, Extension):
Logger.log("e", "Unexpected error trying to create the 'Marketplace' dialog.") Logger.log("e", "Unexpected error trying to create the 'Marketplace' dialog.")
return return
self._restart()
self._dialog.show() self._dialog.show()
# Apply enabled/disabled state to installed plugins # Apply enabled/disabled state to installed plugins
@ -234,7 +261,7 @@ class Toolbox(QObject, Extension):
if not plugin_path: if not plugin_path:
return None return None
path = os.path.join(plugin_path, "resources", "qml", qml_name) path = os.path.join(plugin_path, "resources", "qml", qml_name)
dialog = self._application.createQmlComponent(path, {"toolbox": self}) dialog = self._application.createQmlComponent(path, {"toolbox": self})
if not dialog: if not dialog:
raise Exception("Failed to create Marketplace dialog") raise Exception("Failed to create Marketplace dialog")
@ -328,7 +355,7 @@ class Toolbox(QObject, Extension):
self._package_used_qualities = package_used_qualities self._package_used_qualities = package_used_qualities
# Ask change to default material / profile # Ask change to default material / profile
if self._confirm_reset_dialog is None: if self._confirm_reset_dialog is None:
self._confirm_reset_dialog = self._createDialog("ToolboxConfirmUninstallResetDialog.qml") self._confirm_reset_dialog = self._createDialog("dialogs/ToolboxConfirmUninstallResetDialog.qml")
self.uninstallVariablesChanged.emit() self.uninstallVariablesChanged.emit()
if self._confirm_reset_dialog is None: if self._confirm_reset_dialog is None:
Logger.log("e", "ToolboxConfirmUninstallResetDialog should have been initialized, but it is not. Not showing dialog and not uninstalling package.") Logger.log("e", "ToolboxConfirmUninstallResetDialog should have been initialized, but it is not. Not showing dialog and not uninstalling package.")
@ -618,7 +645,7 @@ class Toolbox(QObject, Extension):
if not self._models[response_type]: if not self._models[response_type]:
Logger.log("e", "Could not find the %s model.", response_type) Logger.log("e", "Could not find the %s model.", response_type)
break break
self._server_response_data[response_type] = json_data["data"] self._server_response_data[response_type] = json_data["data"]
self._models[response_type].setMetadata(self._server_response_data[response_type]) self._models[response_type].setMetadata(self._server_response_data[response_type])
@ -630,6 +657,10 @@ class Toolbox(QObject, Extension):
elif response_type == "authors": elif response_type == "authors":
self._models[response_type].setFilter({"package_types": "material"}) self._models[response_type].setFilter({"package_types": "material"})
self._models[response_type].setFilter({"tags": "generic"}) self._models[response_type].setFilter({"tags": "generic"})
elif response_type == "updates":
# Tell the package manager that there's a new set of updates available.
packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]])
self._package_manager.setPackagesWithUpdate(packages)
self.metadataChanged.emit() self.metadataChanged.emit()
@ -660,7 +691,7 @@ class Toolbox(QObject, Extension):
self.setIsDownloading(False) self.setIsDownloading(False)
self._download_reply = cast(QNetworkReply, self._download_reply) self._download_reply = cast(QNetworkReply, self._download_reply)
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
# Check if the download was sucessfull # Check if the download was sucessfull
if self._download_reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: if self._download_reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
try: try:

View file

@ -15,7 +15,6 @@ from cura.Settings.GlobalStack import GlobalStack
from .CloudApiClient import CloudApiClient from .CloudApiClient import CloudApiClient
from .CloudOutputDevice import CloudOutputDevice from .CloudOutputDevice import CloudOutputDevice
from ..Models.Http.CloudClusterResponse import CloudClusterResponse from ..Models.Http.CloudClusterResponse import CloudClusterResponse
from ..Messages.CloudPrinterDetectedMessage import CloudPrinterDetectedMessage
## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
@ -111,7 +110,6 @@ class CloudOutputDeviceManager:
) )
self._remote_clusters[device.getId()] = device self._remote_clusters[device.getId()] = device
self.discoveredDevicesChanged.emit() self.discoveredDevicesChanged.emit()
self._checkIfNewClusterWasAdded(device.clusterData.cluster_id)
self._connectToActiveMachine() self._connectToActiveMachine()
def _onDiscoveredDeviceUpdated(self, cluster_data: CloudClusterResponse) -> None: def _onDiscoveredDeviceUpdated(self, cluster_data: CloudClusterResponse) -> None:
@ -183,11 +181,4 @@ class CloudOutputDeviceManager:
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
if device.key not in output_device_manager.getOutputDeviceIds(): if device.key not in output_device_manager.getOutputDeviceIds():
output_device_manager.addOutputDevice(device) output_device_manager.addOutputDevice(device)
## Checks if Cura has a machine stack (printer) for the given cluster ID and shows a message if it hasn't.
def _checkIfNewClusterWasAdded(self, cluster_id: str) -> None:
container_registry = CuraApplication.getInstance().getContainerRegistry()
cloud_machines = container_registry.findContainersMetadata(**{self.META_CLUSTER_ID: "*"}) # all cloud machines
if not any(machine[self.META_CLUSTER_ID] == cluster_id for machine in cloud_machines):
CloudPrinterDetectedMessage().show()

View file

@ -1,45 +0,0 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM import i18nCatalog
from UM.Message import Message
from cura.CuraApplication import CuraApplication
I18N_CATALOG = i18nCatalog("cura")
## Message shown when a new printer was added to your account but not yet in Cura.
class CloudPrinterDetectedMessage(Message):
# Singleton used to prevent duplicate messages of this type at the same time.
__is_visible = False
# Store in preferences to hide this message in the future.
_preference_key = "cloud/block_new_printers_popup"
def __init__(self) -> None:
super().__init__(
title=I18N_CATALOG.i18nc("@info:title", "New cloud printers found"),
text=I18N_CATALOG.i18nc("@info:message", "New printers have been found connected to your account, "
"you can find them in your list of discovered printers."),
lifetime=0,
dismissable=True,
option_state=False,
option_text=I18N_CATALOG.i18nc("@info:option_text", "Do not show this message again")
)
self.optionToggled.connect(self._onDontAskMeAgain)
CuraApplication.getInstance().getPreferences().addPreference(self._preference_key, False)
def show(self) -> None:
if CuraApplication.getInstance().getPreferences().getValue(self._preference_key):
return
if CloudPrinterDetectedMessage.__is_visible:
return
super().show()
CloudPrinterDetectedMessage.__is_visible = True
def hide(self, send_signal = True) -> None:
super().hide(send_signal)
CloudPrinterDetectedMessage.__is_visible = False
def _onDontAskMeAgain(self, checked: bool) -> None:
CuraApplication.getInstance().getPreferences().setValue(self._preference_key, checked)

View file

@ -2,8 +2,6 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional, Union, Dict, Any from typing import List, Optional, Union, Dict, Any
from PyQt5.QtCore import QUrl
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
from .ClusterBuildPlate import ClusterBuildPlate from .ClusterBuildPlate import ClusterBuildPlate

View file

@ -43,7 +43,7 @@ class ZeroConfClient:
Logger.logException("e", "Failed to create zeroconf instance.") Logger.logException("e", "Failed to create zeroconf instance.")
return return
self._service_changed_request_thread = Thread(target = self._handleOnServiceChangedRequests, daemon = True) self._service_changed_request_thread = Thread(target = self._handleOnServiceChangedRequests, daemon = True, name = "ZeroConfServiceChangedThread")
self._service_changed_request_thread.start() self._service_changed_request_thread.start()
self._zero_conf_browser = ServiceBrowser(self._zero_conf, self.ZERO_CONF_NAME, [self._queueService]) self._zero_conf_browser = ServiceBrowser(self._zero_conf, self.ZERO_CONF_NAME, [self._queueService])

View file

@ -61,7 +61,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600] self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600]
# Instead of using a timer, we really need the update to be as a thread, as reading from serial can block. # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block.
self._update_thread = Thread(target = self._update, daemon = True) self._update_thread = Thread(target = self._update, daemon = True, name = "USBPrinterUpdate")
self._last_temperature_request = None # type: Optional[int] self._last_temperature_request = None # type: Optional[int]
self._firmware_idle_count = 0 self._firmware_idle_count = 0
@ -212,7 +212,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._serial.close() self._serial.close()
# Re-create the thread so it can be started again later. # Re-create the thread so it can be started again later.
self._update_thread = Thread(target=self._update, daemon=True) self._update_thread = Thread(target=self._update, daemon=True, name = "USBPrinterUpdate")
self._serial = None self._serial = None
## Send a command to printer. ## Send a command to printer.

View file

@ -6,7 +6,7 @@ import io #To write config files to strings as if they were files.
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
import UM.VersionUpgrade import UM.VersionUpgrade
from UM.Logger import Logger
## Creates a new profile instance by parsing a serialised profile in version 1 ## Creates a new profile instance by parsing a serialised profile in version 1
# of the file format. # of the file format.
@ -20,6 +20,7 @@ def importFrom(serialised: str, filename: str) -> Optional["Profile"]:
except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
return None return None
## A representation of a profile used as intermediary form for conversion from ## A representation of a profile used as intermediary form for conversion from
# one format to the other. # one format to the other.
class Profile: class Profile:

View file

@ -325,7 +325,9 @@ class VersionUpgrade41to42(VersionUpgrade):
material_id = parser["containers"]["3"] material_id = parser["containers"]["3"]
old_quality_id = parser["containers"]["2"] old_quality_id = parser["containers"]["2"]
if material_id in _creality_quality_per_material and old_quality_id in _creality_quality_per_material[material_id]: if material_id in _creality_quality_per_material and old_quality_id in _creality_quality_per_material[material_id]:
parser["containers"]["2"] = _creality_quality_per_material[material_id][old_quality_id] if definition_id == "creality_cr10_extruder_0": # We can't disambiguate between Creality CR-10 and Creality-CR10S since they share the same extruder definition. Have to go by the name.
if "cr-10s" in parser["metadata"].get("machine", "Creality CR-10").lower(): # Not perfect, since the user can change this name :(
parser["containers"]["2"] = _creality_quality_per_material[material_id][old_quality_id]
stack_copy = {} # type: Dict[str, str] # Make a copy so that we don't modify the dict we're iterating over. stack_copy = {} # type: Dict[str, str] # Make a copy so that we don't modify the dict we're iterating over.
stack_copy.update(parser["containers"]) stack_copy.update(parser["containers"])

View file

@ -66,6 +66,13 @@ class VersionUpgrade43to44(VersionUpgrade):
# Alternate skin rotation should be translated to top/bottom line directions. # Alternate skin rotation should be translated to top/bottom line directions.
if "skin_alternate_rotation" in parser["values"] and parseBool(parser["values"]["skin_alternate_rotation"]): if "skin_alternate_rotation" in parser["values"] and parseBool(parser["values"]["skin_alternate_rotation"]):
parser["values"]["skin_angles"] = "[45, 135, 0, 90]" parser["values"]["skin_angles"] = "[45, 135, 0, 90]"
# Unit of adaptive layers topography size changed.
if "adaptive_layer_height_threshold" in parser["values"]:
val = parser["values"]["adaptive_layer_height_threshold"]
if val.startswith("="):
val = val[1:]
val = "=({val}) / 1000".format(val = val) # Convert microns to millimetres. Works even if the profile contained a formula.
parser["values"]["adaptive_layer_height_threshold"] = val
result = io.StringIO() result = io.StringIO()
parser.write(result) parser.write(result)

View file

@ -10,7 +10,7 @@ definition = creality_cr10s
[metadata] [metadata]
type = definition_changes type = definition_changes
setting_version = 10 setting_version = 11
[values] [values]
%s %s

View file

@ -720,6 +720,8 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material._dirty = False new_hotend_material._dirty = False
if is_new_material: if is_new_material:
if ContainerRegistry.getInstance().isReadOnly(self.getId()):
ContainerRegistry.getInstance().setExplicitReadOnly(new_hotend_material.getId())
containers_to_add.append(new_hotend_material) containers_to_add.append(new_hotend_material)
# there is only one ID for a machine. Once we have reached here, it means we have already found # there is only one ID for a machine. Once we have reached here, it means we have already found

13
public_key.pem Normal file
View file

@ -0,0 +1,13 @@
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA8k8IJsNNM097VM2pJ5vxkHcLhHf76JCB0iyvqpUuIgl8Zcp78Go+
WtVkbVBZPPfSSB8GwjEtxvZeWj3i6e3nfreuuzq2sw6Gh860wMiQbNgL+rYCU3m9
XxvC0kXgZt+oYs13N5LTePV7BG4goa/JOcN8dsu2ptZKfgH6TPhwshMeOGr/RoGr
Jw1DrpvVeq/yTkrEHQHdtHr81GDghfK1vzxYQCt94MOFQCeShhtIC/jHelenJA94
EpXqcWwCzFDfCQ3aXmCNHnMAsTHer7DWDfvsaUFyvJznrxkuQZIOQydGCNWhePTw
nGiaMydchknr9TT3F+W/yuCs4u5GdZsz7S+1qbG4hblXo6dV6CTzkdKhh/MzONPC
w6u1QBHUeTWN98zcTdtGIn53jjZEyYTodPnw/p4xLHVCju78a7uwm5U0rahcs6gw
658glo3uT41mmTrXTBIVTV+4f/dSrwJVpNfTy/E4wi6fiuFeN8ojqXqN+NbIymfJ
aKar/Jf/nM3QpEYaPz7yyn8PW8MZ7iomqnsPzyQGE1aymuEbw0ipTzMB7Oy/DfuU
d4JU8FFuVuWJj3zNaXW7U/ggzbt5vkdIP/VNVfNZf741J/yKRbCI0+j4mthbruVQ
Ka4aB2EVp1ozisHMaALg5tAeUgrQDZjGnVmSQLt+yFUUbG4e0XFQBb8CAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -6,7 +6,7 @@
"type": "extruder", "type": "extruder",
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Unknown", "manufacturer": "Unknown",
"setting_version": 10, "setting_version": 11,
"visible": false, "visible": false,
"position": "0" "position": "0"
}, },

View file

@ -7,7 +7,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"category": "Other", "category": "Other",
"manufacturer": "Unknown", "manufacturer": "Unknown",
"setting_version": 10, "setting_version": 11,
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g", "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
"visible": false, "visible": false,
"has_materials": true, "has_materials": true,
@ -387,6 +387,16 @@
"settable_per_extruder": false, "settable_per_extruder": false,
"settable_per_meshgroup": false "settable_per_meshgroup": false
}, },
"machine_extruders_share_heater":
{
"label": "Extruders Share Heater",
"description": "Whether the extruders share a single heater rather than each extruder having its own heater.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_disallowed_areas": "machine_disallowed_areas":
{ {
"label": "Disallowed Areas", "label": "Disallowed Areas",
@ -2144,7 +2154,7 @@
"minimum_value": "-273.15", "minimum_value": "-273.15",
"minimum_value_warning": "material_standby_temperature", "minimum_value_warning": "material_standby_temperature",
"maximum_value_warning": "material_print_temperature", "maximum_value_warning": "material_print_temperature",
"enabled": "machine_nozzle_temp_enabled", "enabled": "machine_nozzle_temp_enabled and not machine_extruders_share_heater",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
}, },
@ -2159,7 +2169,7 @@
"minimum_value": "-273.15", "minimum_value": "-273.15",
"minimum_value_warning": "material_standby_temperature", "minimum_value_warning": "material_standby_temperature",
"maximum_value_warning": "material_print_temperature", "maximum_value_warning": "material_print_temperature",
"enabled": "machine_nozzle_temp_enabled", "enabled": "machine_nozzle_temp_enabled and not machine_extruders_share_heater",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
}, },
@ -7046,11 +7056,12 @@
}, },
"adaptive_layer_height_threshold": "adaptive_layer_height_threshold":
{ {
"label": "Adaptive Layers Threshold", "label": "Adaptive Layers Topography Size",
"description": "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer.", "description": "Target horizontal distance between two adjacent layers. Reducing this setting causes thinner layers to be used to bring the edges of the layers closer together.",
"type": "float", "type": "float",
"enabled": "adaptive_layer_height_enabled", "enabled": "adaptive_layer_height_enabled",
"default_value": 200.0, "default_value": 0.2,
"unit": "mm",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false, "settable_per_extruder": false,
"settable_per_meshgroup": false "settable_per_meshgroup": false
@ -7346,6 +7357,7 @@
"description": "Retract the filament when the nozzle is moving over a non-printed area.", "description": "Retract the filament when the nozzle is moving over a non-printed area.",
"type": "bool", "type": "bool",
"default_value": true, "default_value": true,
"value": "retraction_enable",
"enabled": "clean_between_layers", "enabled": "clean_between_layers",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -7358,6 +7370,7 @@
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"value": "retraction_amount",
"minimum_value_warning": "-0.0001", "minimum_value_warning": "-0.0001",
"maximum_value_warning": "10.0", "maximum_value_warning": "10.0",
"enabled": "wipe_retraction_enable and clean_between_layers", "enabled": "wipe_retraction_enable and clean_between_layers",
@ -7372,6 +7385,7 @@
"unit": "mm³", "unit": "mm³",
"type": "float", "type": "float",
"default_value": 0, "default_value": 0,
"value": "retraction_extra_prime_amount",
"minimum_value_warning": "-0.0001", "minimum_value_warning": "-0.0001",
"maximum_value_warning": "10.0", "maximum_value_warning": "10.0",
"enabled": "wipe_retraction_enable and clean_between_layers", "enabled": "wipe_retraction_enable and clean_between_layers",
@ -7385,6 +7399,7 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"default_value": 5, "default_value": 5,
"value": "retraction_speed",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "1", "minimum_value_warning": "1",
"maximum_value": "machine_max_feedrate_e", "maximum_value": "machine_max_feedrate_e",
@ -7406,13 +7421,13 @@
"minimum_value_warning": "1", "minimum_value_warning": "1",
"maximum_value_warning": "70", "maximum_value_warning": "70",
"enabled": "wipe_retraction_enable and clean_between_layers", "enabled": "wipe_retraction_enable and clean_between_layers",
"value": "retraction_speed", "value": "wipe_retraction_speed",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
}, },
"wipe_retraction_prime_speed": "wipe_retraction_prime_speed":
{ {
"label": "Retraction Prime Speed", "label": "Wipe Retraction Prime Speed",
"description": "The speed at which the filament is primed during a wipe retraction move.", "description": "The speed at which the filament is primed during a wipe retraction move.",
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
@ -7422,7 +7437,7 @@
"minimum_value_warning": "1", "minimum_value_warning": "1",
"maximum_value_warning": "70", "maximum_value_warning": "70",
"enabled": "wipe_retraction_enable and clean_between_layers", "enabled": "wipe_retraction_enable and clean_between_layers",
"value": "retraction_speed", "value": "wipe_retraction_speed",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
} }
@ -7443,10 +7458,11 @@
}, },
"wipe_hop_enable": "wipe_hop_enable":
{ {
"label": "Wipe Z Hop When Retracted", "label": "Wipe Z Hop",
"description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.", "description": "When wiping, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.",
"type": "bool", "type": "bool",
"default_value": true, "default_value": true,
"value": "retraction_hop_enabled",
"enabled": "clean_between_layers", "enabled": "clean_between_layers",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -7459,6 +7475,7 @@
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"value": "retraction_hop",
"enabled": "wipe_hop_enable and clean_between_layers", "enabled": "wipe_hop_enable and clean_between_layers",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -7470,7 +7487,8 @@
"description": "Speed to move the z-axis during the hop.", "description": "Speed to move the z-axis during the hop.",
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"default_value": 100, "default_value": 10,
"value": "speed_z_hop",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "1", "minimum_value_warning": "1",
"enabled": "wipe_hop_enable and clean_between_layers", "enabled": "wipe_hop_enable and clean_between_layers",
@ -7523,18 +7541,21 @@
"type": "float", "type": "float",
"minimum_value": "0", "minimum_value": "0",
"default_value": 0, "default_value": 0,
"settable_per_mesh": true "settable_per_mesh": true,
}, "children":
"small_feature_max_length": {
{ "small_feature_max_length":
"label": "Small Feature Max Length", {
"description": "Feature outlines that are shorter than this length will be printed using Small Feature Speed.", "label": "Small Feature Max Length",
"unit": "mm", "description": "Feature outlines that are shorter than this length will be printed using Small Feature Speed.",
"type": "float", "unit": "mm",
"minimum_value": "0", "type": "float",
"default_value": 0, "minimum_value": "0",
"value": "small_hole_max_size * math.pi", "default_value": 0,
"settable_per_mesh": true "value": "small_hole_max_size * math.pi",
"settable_per_mesh": true
}
}
}, },
"small_feature_speed_factor": "small_feature_speed_factor":
{ {

View file

@ -18,12 +18,12 @@
"supports_usb_connection": false, "supports_usb_connection": false,
"machine_extruder_trains": "machine_extruder_trains":
{ {
"0": "alya3dp_extruder_0" "0": "kupido_extruder_0"
} }
}, },
"overrides": { "overrides": {
"machine_name": { "default_value": "ALYA 3DP" }, "machine_name": { "default_value": "KUPIDO" },
"machine_heated_bed": { "default_value": true }, "machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 195 }, "machine_width": { "default_value": 195 },
"machine_height": { "default_value": 190 }, "machine_height": { "default_value": 190 },

View file

@ -0,0 +1,115 @@
{
"version": 2,
"name": "Leapfrog Bolt Pro",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Karan and Vincent 20191104",
"manufacturer": "Leapfrog B.V.",
"category": "Other",
"platform": "leapfrog_bolt_pro_platform.stl",
"platform_offset": [0, 0, -14],
"file_formats": "text/x-gcode",
"supports_usb_connection": false,
"supports_network_connection": false,
"has_materials": true,
"has_machine_quality": true,
"has_variants": true,
"preferred_variant_name": "Brass 0.4",
"preferred_material": "leapfrog_epla_natural",
"variants_name": "Hot end",
"exclude_materials": [
"generic_pla_175",
"generic_abs_175",
"generic_cpe_175",
"generic_hips_175",
"generic_nylon_175",
"generic_pc_175",
"generic_petg_175",
"generic_pva_175",
"generic_tpu_175",
"chromatik_pla",
"dsm_arnitel2045_175",
"dsm_novamid1070_175",
"emotiontech_abs",
"emotiontech_petg",
"emotiontech_pla",
"emotiontech_pva-m",
"emotiontech_pva-oks",
"emotiontech_pva-s",
"emotiontech_tpu98a",
"fabtotum_abs",
"fabtotum_nylon",
"fabtotum_pla",
"fabtotum_tpu",
"fiberlogy_hd_pla",
"filo3d_pla",
"filo3d_pla_green",
"filo3d_pla_red",
"imade3d_petg_175",
"imade3d_pla_175",
"innofill_innoflex60_175",
"octofiber_pla",
"polyflex_pla",
"polymax_pla",
"polyplus_pla",
"polywood_pla",
"tizyx_abs",
"tizyx_pla",
"tizyx_flex",
"tizyx_petg",
"tizyx_pva",
"tizyx_pla_bois",
"verbatim_bvoh_175",
"Vertex_Delta_ABS",
"Vertex_Delta_PET",
"Vertex_Delta_PLA_Glitter",
"Vertex_Delta_PLA_Mat",
"Vertex_Delta_PLA_Satin",
"Vertex_Delta_PLA_Wood",
"Vertex_Delta_PLA",
"Vertex_Delta_TPU",
"zyyx_pro_flex",
"zyyx_pro_pla"
],
"machine_extruder_trains":
{
"0": "leapfrog_bolt_pro_extruder_right",
"1": "leapfrog_bolt_pro_extruder_left"
}
},
"overrides": {
"machine_name": {"default_value": "Leapfrog Bolt Pro" },
"machine_extruder_count": {"default_value": 2},
"machine_center_is_zero": {"default_value": false},
"machine_width": {"default_value": 302},
"machine_height": {"default_value": 205},
"machine_depth": {"default_value": 322},
"machine_heated_bed": {"default_value": true},
"machine_head_with_fans_polygon": {"default_value": [[-60, 110 ], [-60, -45], [60, -45 ], [60, 110]]},
"machine_max_feedrate_z": {"default_value": 16.7 },
"machine_max_feedrate_e": {"default_value": 50 },
"machine_max_acceleration_z": {"default_value": 100 },
"machine_acceleration": {"default_value": 400 },
"machine_max_jerk_xy": {"default_value": 20 },
"machine_max_jerk_z": {"default_value": 0.4 },
"machine_max_jerk_e": {"default_value": 5 },
"machine_gcode_flavor": {"default_value": "RepRap (Marlin/Sprinter)"},
"material_final_print_temperature": {"value": "default_material_print_temperature" },
"material_initial_print_temperature": {"value": "default_material_print_temperature" },
"gantry_height": {"value": "20"},
"retraction_combing": { "default_value": "all" },
"retraction_amount": {"default_value": 2},
"adhesion_type": {"default_value": "skirt"},
"skirt_line_count": {"default_value": 3},
"machine_use_extruder_offset_to_offset_coords": {"default_value": true},
"machine_start_gcode": {"default_value": "G90\nG28 X0 Y0 Z0\nG1 Z5 F1000\nG92 E0\nG1 Y-32 F12000\nG1 E15 F1000\nG1 E45 F150\nG4 S5"},
"machine_end_gcode": {"default_value": "G92 E0\nG1 E-3 F300\nM104 S0 T0\nM104 S0 T1\nM140 S0\nG28 X0 Y0\nM84"},
"prime_tower_enable": { "resolve": "extruders_enabled_count > 1"},
"prime_tower_position_x": {"value": "169" },
"prime_tower_position_y": {"value": "25" },
"speed_travel": { "value": "200" },
"build_volume_temperature": {"enabled": false},
"material_standby_temperature": {"enabled": false }
}
}

View file

@ -0,0 +1,68 @@
{
"version": 2,
"name": "Makeblock mCreate",
"inherits": "fdmprinter",
"metadata": {
"author": "Makeblock",
"manufacturer": "Makeblock",
"visible": true,
"file_formats": "application/gzip;text/x-gcode",
"has_machine_quality": true,
"preferred_quality_type": "normal",
"machine_extruder_trains": {
"0": "makeblock_mcreate_extruder_0"
}
},
"overrides": {
"machine_name": {
"default_value": "Makeblock mCreate"
},
"machine_width": {
"default_value": 225
},
"machine_depth": {
"default_value": 225
},
"machine_height": {
"default_value": 300
},
"machine_heated_bed": {
"default_value": true
},
"machine_head_with_fans_polygon": {
"default_value": [
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
]
]
},
"machine_gcode_flavor": {
"default_value": "Marlin"
},
"gantry_height": {
"value": 15.0
},
"machine_extruder_count": {
"default_value": 1
},
"machine_start_gcode": {
"default_value": "; Mcreate Start Gcode \nG28 ; Home all axes \nG92 E0 ; Reset Extruder\nG1 X0 Y0 Z15 F3000.0 ; Move to start position \nG1 E10 F400 ;load filament \nG1 E2 F400 ;retarct filament \nG92 E0 ; Reset Extruder \nG1 X0 Y130 Z15 F3000.0 \nG12 ; clean nozzle \nG1 X0 Y0 Z0.3 F3000.0 ; Move to start position \nG1 E9.0 F400 ;loadsome filament \nG92 E0 ; Reset Extruder \n; End of start GCode"
},
"machine_end_gcode": {
"default_value": "; Mcreate end Gcode \nG4 ; Wait command in buffer have finished \nG92 E0 \nG1 E-2 F300; retract filament \nG28 X Z; home x z axis \nG1 F3000 Y220;Move Heat Bed to the front for easy print removal \nM104 S0; Turn off the nozzle heat \nM140 S0; Turn off the bed heat \nM107 ; Turn off the Fan \nM84 ; Disable stepper motors \n; End of GCode"
}
}
}

View file

@ -0,0 +1,598 @@
{
"name": "Skriware 2",
"version": 2,
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Skriware",
"manufacturer": "Skriware",
"category": "Other",
"file_formats": "text/x-gcode",
"platform_offset": [
0,
0,
0
],
"supports_usb_connection": false,
"platform": "skriware_2_platform.stl",
"machine_extruder_trains": {
"0": "skriware_2_extruder_0",
"1": "skriware_2_extruder_1"
}
},
"overrides": {
"jerk_print_layer_0": {
"value": "5"
},
"jerk_prime_tower": {
"value": "5"
},
"expand_skins_expand_distance": {
"value": "1.2"
},
"jerk_support_interface": {
"value": "5"
},
"jerk_travel_layer_0": {
"value": "5.0"
},
"wipe_retraction_prime_speed": {
"value": "30"
},
"material_standby_temperature": {
"default_value": 195
},
"acceleration_support_bottom": {
"value": "250"
},
"raft_base_line_width": {
"value": "0.5"
},
"raft_speed": {
"value": "30.0"
},
"jerk_topbottom": {
"value": "5"
},
"ironing_inset": {
"value": "0.2"
},
"acceleration_wall": {
"value": "250"
},
"cross_infill_pocket_size": {
"value": "5.333333333333333"
},
"jerk_support_roof": {
"value": "5"
},
"acceleration_print": {
"default_value": 250
},
"meshfix_maximum_travel_resolution": {
"value": "0.8"
},
"support_top_distance": {
"value": "0.22"
},
"acceleration_enabled": {
"default_value": true
},
"optimize_wall_printing_order": {
"default_value": true
},
"jerk_layer_0": {
"value": "5"
},
"infill_line_distance": {
"value": "5.333333333333333"
},
"acceleration_ironing": {
"value": "250"
},
"material_print_temperature_layer_0": {
"value": "195"
},
"bridge_skin_speed_2": {
"value": "15"
},
"acceleration_travel": {
"value": "250"
},
"switch_extruder_retraction_speed": {
"value": "30"
},
"jerk_print": {
"default_value": 5
},
"material_guid": {
"default_value": "0ff92885-617b-4144-a03c-9989872454bc"
},
"raft_interface_acceleration": {
"value": "250"
},
"acceleration_support_interface": {
"value": "250"
},
"cool_fan_full_layer": {
"value": "1"
},
"skirt_brim_minimal_length": {
"default_value": 50
},
"material_bed_temperature": {
"value": "50"
},
"speed_slowdown_layers": {
"default_value": 1
},
"speed_travel": {
"value": "150"
},
"skin_overlap": {
"value": "15"
},
"acceleration_infill": {
"value": "250"
},
"support_roof_material_flow": {
"value": "99"
},
"raft_base_jerk": {
"value": "5"
},
"retraction_retract_speed": {
"value": "30"
},
"infill_wipe_dist": {
"value": "0.1"
},
"jerk_wall_x": {
"value": "5"
},
"layer_height": {
"default_value": 0.2
},
"bottom_skin_expand_distance": {
"value": "1.2000000000000002"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 X0 Y0;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM420 S1 Z0.9 ;enable bed levelling\nG1 Z10 F250 ;move the platform down 10mm\nM107 ;fan off\nM42 P11 S255 ;turn on front fan\nM140 S{material_bed_temperature}\nM104 T0 S{material_print_temperature}\nM104 T1 S{material_print_temperature}\nG1 F2500 Y260\nM190 S{material_bed_temperature}\nM109 T0 S{material_print_temperature}\nM109 T1 S{material_print_temperature}\nM60 ;enable E-FADE Algorithm\nM62 A ;filament sensor off\nG92 E0 ;zero the extruded length\nT1\nG92 E0;zero the extruded length\nG1 F300 Z0.3\nG1 F1200 X20\nG1 F1200 X180 E21 ;extrude 21 mm of feed stock\nG1 F1200 E15 ;retracting 6 mm\nG1 F2000 E21\nG1 F2000 E15\nG1 F300 Z1.5\nG92 E0 ;zero the extruded length again\nT0\nG92 E0 ;zero the extruded length\nG1 F1200 Y258\nG1 F300 Z0.3\nG1 F1200 X40 E21 ;extrude 21 mm of feed stock\nG1 F1200 E15 ;retracting 6 mm\nG1 F2000 E21\nG1 F2000 E15\nG1 Z1.5\nM61 A\nM63 A ;filament sensor on\nG92 E0 ;zero the extruded length again\nM58 ;end of Start G-Code and signal retract management"
},
"travel_retract_before_outer_wall": {
"default_value": true
},
"xy_offset_layer_0": {
"value": "-0.16"
},
"adhesion_type": {
"default_value": "raft"
},
"min_skin_width_for_expansion": {
"value": "0.671279704941824"
},
"support_bottom_material_flow": {
"value": "99"
},
"prime_tower_position_x": {
"value": "1"
},
"machine_depth": {
"default_value": 260
},
"retraction_speed": {
"default_value": 30
},
"support_skip_some_zags": {
"default_value": true
},
"remove_empty_first_layers": {
"default_value": false
},
"z_seam_x": {
"value": "115"
},
"support_xy_distance_overhang": {
"value": "0.5"
},
"support_tree_wall_thickness": {
"value": "0.4"
},
"acceleration_print_layer_0": {
"value": "250"
},
"support_xy_distance": {
"default_value": 0.8
},
"support_roof_line_distance": {
"value": "0.5714285714285714"
},
"jerk_enabled": {
"default_value": true
},
"min_infill_area": {
"default_value": 1
},
"travel_avoid_supports": {
"default_value": true
},
"bottom_layers": {
"value": "3"
},
"multiple_mesh_overlap": {
"default_value": 0
},
"retraction_hop_enabled": {
"default_value": true
},
"acceleration_topbottom": {
"value": "250"
},
"jerk_wall": {
"value": "5"
},
"jerk_wall_0": {
"value": "5"
},
"skin_overlap_mm": {
"value": "0.06"
},
"retraction_min_travel": {
"value": "1"
},
"support_interface_material_flow": {
"value": "99"
},
"material_diameter": {
"default_value": 1.75
},
"speed_roofing": {
"value": "30.0"
},
"skin_outline_count": {
"default_value": 0
},
"skin_no_small_gaps_heuristic": {
"default_value": true
},
"top_bottom_pattern_0": {
"value": "'zigzag'"
},
"top_skin_expand_distance": {
"value": "1.2000000000000002"
},
"acceleration_travel_layer_0": {
"value": "250.0"
},
"prime_tower_min_volume": {
"default_value": 4
},
"switch_extruder_retraction_speeds": {
"default_value": 30
},
"skin_preshrink": {
"value": "1.2000000000000002"
},
"material_bed_temperature_layer_0": {
"value": "50"
},
"support_tree_collision_resolution": {
"value": "0.2"
},
"machine_height": {
"default_value": 210
},
"raft_acceleration": {
"value": "250"
},
"fill_outline_gaps": {
"default_value": true
},
"wall_x_material_flow": {
"value": "99"
},
"jerk_support_bottom": {
"value": "5"
},
"machine_end_gcode": {
"default_value": "M59\nG92 E1\nG1 E-1 F300\nM104 T0 S0\nM104 T1 S0\nM140 S0\nG28 X0 Y0\nM84\nM106 S0\nM107"
},
"infill_sparse_density": {
"default_value": 15
},
"meshfix_maximum_deviation": {
"default_value": 0.005
},
"wall_0_material_flow": {
"value": "99"
},
"material_adhesion_tendency": {
"default_value": 0
},
"prime_tower_flow": {
"value": "99"
},
"prime_tower_position_y": {
"value": "1"
},
"support_material_flow": {
"value": "99"
},
"retract_at_layer_change": {
"default_value": true
},
"machine_extruder_count": {
"default_value": 2
},
"wall_thickness": {
"default_value": 1.2
},
"support_infill_sparse_thickness": {
"value": "0.2"
},
"raft_surface_acceleration": {
"value": "250"
},
"roofing_layer_count": {
"value": "1"
},
"skirt_brim_line_width": {
"value": "0.5"
},
"jerk_support": {
"value": "5"
},
"raft_surface_jerk": {
"value": "5"
},
"speed_equalize_flow_max": {
"default_value": 40
},
"raft_surface_speed": {
"value": "30.0"
},
"jerk_travel": {
"value": "5"
},
"support_zag_skip_count": {
"value": "8"
},
"retraction_combing": {
"default_value": "infill"
},
"raft_interface_line_spacing": {
"value": "0.4"
},
"layer_height_0": {
"default_value": 0.2
},
"extruders_enabled_count": {
"value": "2"
},
"support_line_distance": {
"value": "1.3333333333333333"
},
"support_roof_density": {
"value": "70"
},
"raft_base_line_spacing": {
"value": "0.8"
},
"acceleration_prime_tower": {
"value": "250"
},
"skin_material_flow": {
"value": "99"
},
"support_z_distance": {
"default_value": 0.22
},
"bottom_skin_preshrink": {
"value": "1.2000000000000002"
},
"jerk_skirt_brim": {
"value": "5"
},
"z_seam_y": {
"value": "180"
},
"skirt_line_count": {
"default_value": 2
},
"raft_margin": {
"default_value": 4
},
"infill_material_flow": {
"value": "99"
},
"wipe_retraction_retract_speed": {
"value": "30"
},
"z_seam_corner": {
"default_value": "z_seam_corner_weighted"
},
"support_roof_height": {
"value": "0.4"
},
"top_layers": {
"value": "4"
},
"support_infill_rate": {
"value": "30"
},
"raft_interface_speed": {
"value": "35"
},
"default_material_print_temperature": {
"default_value": 195
},
"acceleration_layer_0": {
"value": "250"
},
"support_skip_zag_per_mm": {
"default_value": 10
},
"material_initial_print_temperature": {
"value": "195"
},
"raft_interface_jerk": {
"value": "5"
},
"machine_width": {
"default_value": 210
},
"wall_line_count": {
"value": "3"
},
"retraction_amount": {
"default_value": 3
},
"infill_sparse_thickness": {
"value": "0.2"
},
"support_initial_layer_line_distance": {
"value": "1.3333333333333333"
},
"jerk_support_infill": {
"value": "5"
},
"acceleration_roofing": {
"value": "250"
},
"retraction_extrusion_window": {
"value": "3"
},
"raft_interface_line_width": {
"value": "0.4"
},
"acceleration_support_roof": {
"value": "250"
},
"support_brim_line_count": {
"value": "16"
},
"layer_0_z_overlap": {
"value": "0.1"
},
"support_angle": {
"default_value": 60
},
"machine_heated_bed": {
"default_value": true
},
"raft_surface_thickness": {
"value": "0.2"
},
"cool_min_layer_time": {
"default_value": 10
},
"gantry_height": {
"value": "210"
},
"raft_airgap": {
"default_value": 0.2
},
"acceleration_skirt_brim": {
"value": "250"
},
"skirt_brim_material_flow": {
"value": "99"
},
"jerk_infill": {
"value": "5"
},
"roofing_material_flow": {
"value": "99"
},
"support_use_towers": {
"default_value": false
},
"ooze_shield_angle": {
"default_value": 50
},
"material_flow": {
"default_value": 99
},
"speed_travel_layer_0": {
"value": "75.0"
},
"raft_base_acceleration": {
"value": "250"
},
"retraction_count_max": {
"default_value": 40
},
"ooze_shield_dist": {
"default_value": 4
},
"acceleration_support": {
"value": "250"
},
"max_skin_angle_for_expansion": {
"default_value": 50
},
"coasting_enable": {
"default_value": true
},
"brim_width": {
"default_value": 10
},
"acceleration_support_infill": {
"value": "250"
},
"retraction_prime_speed": {
"value": "30"
},
"raft_base_speed": {
"value": "35"
},
"acceleration_wall_0": {
"value": "250"
},
"xy_offset": {
"default_value": -0.16
},
"prime_tower_size": {
"default_value": 1
},
"jerk_ironing": {
"value": "5"
},
"switch_extruder_prime_speed": {
"value": "30"
},
"raft_jerk": {
"value": "5"
},
"top_skin_preshrink": {
"value": "1.2000000000000002"
},
"material_print_temperature": {
"value": "195"
},
"wall_material_flow": {
"value": "99"
},
"jerk_roofing": {
"value": "5"
},
"cool_fan_full_at_height": {
"value": "0"
},
"acceleration_wall_x": {
"value": "250"
},
"support_bottom_distance": {
"value": "0.23"
},
"cool_min_speed": {
"default_value": 15
},
"default_material_bed_temperature": {
"default_value": 50
},
"raft_interface_thickness": {
"value": "0.2"
}
}
}

View file

@ -67,8 +67,6 @@
"extruder_prime_pos_abs": { "default_value": true }, "extruder_prime_pos_abs": { "default_value": true },
"machine_start_gcode": { "default_value": "" }, "machine_start_gcode": { "default_value": "" },
"machine_end_gcode": { "default_value": "" }, "machine_end_gcode": { "default_value": "" },
"prime_tower_position_x": { "value": "345" },
"prime_tower_position_y": { "value": "222.5" },
"prime_blob_enable": { "enabled": true, "default_value": false }, "prime_blob_enable": { "enabled": true, "default_value": false },
"speed_travel": "speed_travel":

View file

@ -1,45 +1,45 @@
{ {
"version": 2, "version": 2,
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "BIBO2 dual", "machine": "bibo2_dual",
"position": "0" "position": "0"
}, },
"overrides": { "overrides": {
"extruder_nr": { "extruder_nr": {
"default_value": 0, "default_value": 0,
"maximum_value": "1" "maximum_value": "1"
}, },
"material_diameter": { "material_diameter": {
"default_value": 1.75 "default_value": 1.75
}, },
"machine_nozzle_size": { "machine_nozzle_size": {
"default_value": 0.4 "default_value": 0.4
}, },
"machine_nozzle_offset_x": { "machine_nozzle_offset_x": {
"default_value": 0.0 "default_value": 0.0
}, },
"machine_nozzle_offset_y": { "machine_nozzle_offset_y": {
"default_value": 0.0 "default_value": 0.0
}, },
"machine_extruder_start_pos_abs": { "machine_extruder_start_pos_abs": {
"default_value": true "default_value": true
}, },
"machine_extruder_start_pos_x": { "machine_extruder_start_pos_x": {
"value": "prime_tower_position_x" "value": "prime_tower_position_x"
}, },
"machine_extruder_start_pos_y": { "machine_extruder_start_pos_y": {
"value": "prime_tower_position_y" "value": "prime_tower_position_y"
}, },
"machine_extruder_end_pos_abs": { "machine_extruder_end_pos_abs": {
"default_value": true "default_value": true
}, },
"machine_extruder_end_pos_x": { "machine_extruder_end_pos_x": {
"value": "prime_tower_position_x" "value": "prime_tower_position_x"
}, },
"machine_extruder_end_pos_y": { "machine_extruder_end_pos_y": {
"value": "prime_tower_position_y" "value": "prime_tower_position_y"
} }
} }
} }

View file

@ -1,45 +1,45 @@
{ {
"version": 2, "version": 2,
"name": "Extruder 2", "name": "Extruder 2",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "BIBO2 dual", "machine": "bibo2_dual",
"position": "1" "position": "1"
}, },
"overrides": { "overrides": {
"extruder_nr": { "extruder_nr": {
"default_value": 1, "default_value": 1,
"maximum_value": "1" "maximum_value": "1"
}, },
"material_diameter": { "material_diameter": {
"default_value": 1.75 "default_value": 1.75
}, },
"machine_nozzle_size": { "machine_nozzle_size": {
"default_value": 0.4 "default_value": 0.4
}, },
"machine_nozzle_offset_x": { "machine_nozzle_offset_x": {
"default_value": 0.0 "default_value": 0.0
}, },
"machine_nozzle_offset_y": { "machine_nozzle_offset_y": {
"default_value": 0.0 "default_value": 0.0
}, },
"machine_extruder_start_pos_abs": { "machine_extruder_start_pos_abs": {
"default_value": true "default_value": true
}, },
"machine_extruder_start_pos_x": { "machine_extruder_start_pos_x": {
"value": "prime_tower_position_x" "value": "prime_tower_position_x"
}, },
"machine_extruder_start_pos_y": { "machine_extruder_start_pos_y": {
"value": "prime_tower_position_y" "value": "prime_tower_position_y"
}, },
"machine_extruder_end_pos_abs": { "machine_extruder_end_pos_abs": {
"default_value": true "default_value": true
}, },
"machine_extruder_end_pos_x": { "machine_extruder_end_pos_x": {
"value": "prime_tower_position_x" "value": "prime_tower_position_x"
}, },
"machine_extruder_end_pos_y": { "machine_extruder_end_pos_y": {
"value": "prime_tower_position_y" "value": "prime_tower_position_y"
} }
} }
} }

View file

@ -3,7 +3,7 @@
"name": "Left Extruder", "name": "Left Extruder",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creality CR-X", "machine": "creality_cr-x",
"position": "0" "position": "0"
}, },

View file

@ -3,7 +3,7 @@
"name": "Right Extruder", "name": "Right Extruder",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creality CR-X", "machine": "creality_cr-x",
"position": "1" "position": "1"
}, },

View file

@ -3,7 +3,7 @@
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creatable_D3", "machine": "creatable_d3",
"position": "0" "position": "0"
}, },

View file

@ -3,7 +3,7 @@
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "hellbot_magna_i", "machine": "hellbot_magna_I",
"position": "0" "position": "0"
}, },

View file

@ -3,7 +3,7 @@
"name": "0.4mm Nozzle", "name": "0.4mm Nozzle",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Tyro", "machine": "key3d_tyro",
"position": "0" "position": "0"
}, },

View file

@ -0,0 +1,21 @@
{
"version": 2,
"name": "Left extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "leapfrog_bolt_pro",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_head_distance": { "default_value": 22 },
"machine_nozzle_offset_x": { "default_value": 0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_extruder_start_code": { "default_value": "G1 Y-32 F12000\nG1 X6 F1000\nG1 X-32 F4000\nG1 X6" }
}
}

Some files were not shown because too many files have changed in this diff Show more